开局直接疯狂上分的那两队是真的牛逼,这解题速度,都比得上刷过原题了。
不过主办方依然拉跨,刚开始题目全放,我就先去看lcs了,结果回来一看,我tm的reverse和crypto呢?怎么都只剩下一题了...本来以为是上错题了给下了,结果十点多又重新上了?我他妈...那你还不如一开始就别下题了,什么迷惑操作😅

lcs

HNGK-流量分析

打开过滤mms,然后直接搜索flag发现有个列目录的返回结果。

图 1

再往下找有个fileOpen的操作。

图 2

继续往后找也是上面这俩个操作,那么就过滤一下fileOpen的操作去拿打开后的标识符然后看看用哪了。

有4次打开操作。

图 3

那就过滤出这几个的返回包,拿到标识符。

图 4

不过有个返回包没解析出来,先不管,看看能不能用过滤出来的三个标识符找到其fileFead操作。

图 5

成功找到,那就看看这个请求的响应是什么。

结果没过滤出来东西。

图 6

猜测是wireshark没解析成功,那就去掉过滤看看下一个包是什么,这个流量包里的请求和响应都是挨在一起的。

图 7

似乎是因为长度没解析对。

反正看见base64格式的图片了,先提取出来。

发现图片内容就是flag。

图 8

flag{ICS-mms104}

HNGK-MMS

拿到流量包后发现打不开,扔到010发现其实是个压缩包,于是再解压一次。

图 9

遍历一遍mms,发现有两条请求的item比较奇怪。

图 10

66正好是f的ascii。

所以猜测可能是有位移,但是十六进制的字母必然不可能有ij这种东西,所以猜测可能是先把字母往小移,但是不知道具体偏移是多少,那就一个一个试过去,然后移完之后解一下hex看看能出来啥。

先从偏移为4开始,因为至少要4才能让ij移到ef这种合理的hex。

import string

flag = bytearray(b"666i5250356j4249"b"616732557968356j")
for i in range(len(flag)):
    if flag[i] in string.ascii_lowercase.encode():
        flag[i] -= 4
print(flag)
print(bytes.fromhex(flag.decode()))

图 11

但是好像没啥规律,继续尝试到偏移为6的时候发现flag

图 12

尝试将前后半段每两字节拼接,得到flagRP2U5myhBI5m,拼上括号提交发现正确。

flag{RP2U5myhBI5m}

HNGK-modbus

这题筛选modbus之后发现function是有规律循环的:17, 109, 65, 1, 1, 3, 3, 67

图 13

于是尝试提取发送方所有的func_code和帧id,然后跑一轮看看有没有多了或者少了的。

tshark -r .\modbus.pcap -Y "modbus && ip.src==172.31.14.123" -T fields -e "frame.number" -T fields -e "modbus.func_code" > data.txt

发现1648帧多了个1

data = []
with open("data.txt", 'r') as f:
    for line in f:
        data.append(line.strip().split('\t'))

base = ['17', '109', '65', '1', '1', '3', '3', '67']
p = 0
for i in data:
    if i[1] != base[p % len(base)]:
        print(i)
    p += 1

图 14

过去看了下发现确实是多了个1,不过上下对比一下发现实际上多出来的是1642数据包,因为这个1541在其他地方没出现过。

1642数据包在提取结果里是539条,直接忽略这条再跑一遍看看还有没有异常的。

data = []
with open("data.txt", 'r') as f:
    for line in f:
        data.append(line.strip().split('\t'))

base = ['17', '109', '65', '1', '1', '3', '3', '67']
p = 0
for i in data:
    if p == 539:
        continue
    if i[1] != base[p % len(base)]:
        print(i)
    p += 1

直接跑完没有异常。

图 15

尝试提交flag{1642}或者flag{1642+1643}但是都不对。

于是怀疑可能数据包异常点不是func而是其内容。

那么只能挨个func的数据筛选过去看看有没有异常了。

先把17给发送和返回过滤一遍:(modbus.func_code == 17) && !(!modbus.data) && !(modbus.data == 06:00:00)

图 17

发现没有异常的,那就再看109的:((modbus.func_code == 109) && !(modbus.data == 54)) && !(modbus.data == 00)

图 18

发现也没有,再看65的:((modbus.func_code == 65) && !(modbus.data == a8:b9:09:00:0c:01:06:02:06:03:06:04:06:05:06:06:06:07:06:00:06)) && !(modbus.data == 02:fe:00)

图 19

还是没有,再看1的:((((modbus.func_code == 1) && !(modbus.reference_num == 0)) && !(modbus.reference_num == 1536)) && !(frame[63] == 00)) && !(frame[63] == fe)

图 20

发现两对数据包有异常,比之前找的多了一对,所以实际上异常是多了一个和改了一个。

尝试提交flag{1642+5485},发现正确。

flag{1642+5485}

HNGK-奇怪的工控协议

看了下协议分布,最主要的modbus,不过iec60870_104iec60870_asdu也不算少。

图 21

modbus里面翻了半天也没看到啥有用的东西,然后去iec60870_104翻了下直接发现了flag。

图 22

flag{sort__104}

Crypto

HNGK-HardRSA

这题刚开始打算爆破e然后用e, d, n去求p, q,但发现好像不太行。

然后去factordb看了下,发现n居然能直接分解。

图 23

所以直接求flag就完事。

from hashlib import md5
p=8134764250316914977240939055123307507750874306113160101218096577677584025654326282630936230074917597921184142227850055873398652706587349895667411302286629
q=9258376341398185999350718486678388748086924961707902231684477676159974982924771328403762590710189676719483651720152226906035715671461950810512232162187317
print "Flag: flag{%s}" %md5(str(p + q)).hexdigest()

图 24

flag{77d93d7406e76acbd8fc571296beba37}

Reverse

HNGK-cool

拿到个exe,看图标是pyinstaller打包的。

图 25

直接python pyinstxtractor.py cool.exe走一波提取pyc

图 26

打开cool发现文件头缺了一部分,属于是正常情况。

图 27

但是去struct看了下发现也缺了。

图 28

于是去看看PYZ-00.pyz_extracted文件夹里的pyc,发现还是缺了4字节,因为0xe3应该在0x10位置才对。

图 29

然后随便翻了翻,在base_library.zip里面发现了一堆pyc

图 30

这里面文件头的确实是完整的。

图 31

于是给cool补上文件头。

图 32

重命名成cool.pyc,然后去在线解pyc

图 33

感觉缩进不是很对,而且也没有输出啥的,感觉是解的不太对,不过大致逻辑有了,也不复杂,直接写个逆向脚本。

import base64

with open("密文.txt", 'rb') as f:
    y = bytearray(f.read())
for i in range(len(y)):
    if i % 2 == 0:
        y[i] = y[i] ^ 34
        continue
    if i % 3 == 0:
        y[i] = y[i] ^ 51
        continue
    if i % 5 == 0:
        y[i] = y[i] ^ 85
        continue
print(base64.b64decode(y[::-1]))

直接得到flag。

图 34

flag{Pyth0n_is_c001}

HNGK-funning

这题拿到之后发现打不开也没法反编译,结合题目描述猜测是文件头被改了。

幸好作者还给了dll,拿这个去对比两个=文件头就知道啥情况了。

图 35

发现0x3c位置不一样,于是也改成0x80,发现可以正常打开,但是反编译结果感觉像是有壳。

图 36

查壳发现是UPX

图 37

尝试直接脱壳失败,猜测是特征被改过。

图 38

全局找了下UPX,发现有个被改成了FUX

图 39

改回来重新脱壳,成功。

图 40

反编译发现就是enc_dec里面生成完密钥然后直接加密,加密完再在外面来一轮前后异或,最后直接对比加密结果是否预期。

图 41

图 42

然后enc_dec对数据的操作只有自身异或,所以可以直接把预期加密结果先解了外面那个前后异或,再直接动态调试替换数据,应该就能拿到明文。

a = bytearray.fromhex("2f130a833b03fc15ea15d94748a5ba8b4c8bdefffe2290049d0634")
for i in range(24, -3, -3):
    a[i] ^= a[i+1] ^ a[i+2]
print(a.hex())

图 43

所以enc_dec的预期结果应该是36130abb3b030315ea8bd94757a5ba4c4c8bdffffeb69004af0634

直接动调断到输入完成,把输入内容改成上面的预期结果。

图 44

图 45

然后在enc_dec调用完成的地方下个断,直接运行到这就能拿到flag。

图 46

flag{the_rc4_is_funning!!!}

HNGK-guess

这题反编译完的逻辑好像有点问题,不过不太复杂,encode就是标准的base64加密,过程的话应该是先小写转大写,然后全部偏移-63base64完应该就是那一串字符了。所以直接写个逆向脚本按猜测的逻辑来试试。

图 47

b = "Bw0CCDwKByAa8RYgCBYGFBQgAg8FIBUTGj4="
import base64
b = bytearray(base64.b64decode(b))
for i in range(len(b)):
    b[i] = 0xff & (b[i]+63)
    if 0x61-0x20 <= b[i] <= 0x7a-0x20:
        b[i] += 0x20
print(b)

图 48

直接得到flag。

flag{if_y0u_guess_and_try}


The End