前言
2021年“网信柏鹭杯”大学生网络空间安全精英赛
比赛时间:2021年10月24日 9:00 - 17:00
这个比赛怎么说好呢,按往年来说,只有一场比赛,没有线上预选赛,直接线下比一场那种,看起来也比较水吧。(当然去年因为疫情线上了
原计划是于2021年9月13日在厦门举办,结果经大赛组委会决定,延期至10月中旬比赛,报名截止时间延长至9月26日,比赛也变成线上的了。
于是那时候学弟学妹们都报好名了,后来一看时间又延长了,咱就拉上俩佛系队友组了个队,来水一水好了。
结果比赛前两三天队友收到短信通知加wx,说了部分比赛信息,才知道是这周日比赛,离大谱了……
反正大家都很佛系,而且这周末比赛太多了,导致最后只有喵喵自己做了几题出来,唉,喵喵菜菜,喵呜呜(
签到题
浏览器访问会 30x 跳转,于是直接 curl
Misc
Misc1
先拿 Photoshop 修复一波,可以用那个 内容识别填充,高度再稍微复制粘贴加大一点方便扫码。
扫码得到压缩包密码 8521457896541
蜜雪冰城?!
文件打开发现长度贼大,很明显有零宽隐写,拿到 vim 下看一看。
拿 python set 统计一下,发现包含了
{'\u200b',
'\u200c',
'\u200d',
'\u202a',
'\u202c',
'\u202d',
'包',
'压',
'在',
'密',
'就',
'码',
'缩',
'这',
'里',
'\ufeff'}
然后去 零宽隐写 解密,得到一个 mp3
或者 这个 也行,发现还更新了个 UI(
播放发现是 DTMF,内容是 399#9311233212
解压 flag.7z
拿到 flag
flag{ISEC-45rd4r8f4rq6h5y8u2v1x4f5y9i5afde}
Reverse
baby_python
首先确认这个 exe 是用 pyinstaller 打包的,版本是 python 3.7
参考 [CISCN2021]华北区_ctf_re_imnotavirus
用 pyinstxtractor 把 exe 逆向回 pyc
PyInstaller Extractor is a Python script to extract the contents of a PyInstaller generated Windows executable file. The contents of the pyz file (usually pyc files) present inside the executable are also extracted.
The header of the pyc files are automatically fixed so that a Python bytecode decompiler will recognize it. The script can run on both Python 2.x and 3.x. Pyinstaller versions 2.0, 2.1, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.5.1 are tested & supported. Probably will work with other versions too.
python pyinstxtractor.py baby.exe
然而通过提示信息可以发现这些 pyc 都加密了。
然后进入 baby.exe_extracted
文件夹,反编译 pyimod02_archive.pyc
uncompyle6 pyimod02_archive.pyc > pyimod02_archive.py
同理,反编译 baby.pyc
得到主函数逻辑,调用了 baby_python.baby_core
。
在这个 pyimod02_archive.py
基础上解密核心的数据校验部分代码,也就是 PYZ-00.pyz_extracted\baby_python\baby_core.pyc.encrypted
这个文件。
在 pyimod02_archive.py
文件最后面写入
inf = open(r'PYZ-00.pyz_extracted\baby_python\baby_core.pyc.encrypted', 'rb')
c = Cipher()
buf = c.decrypt(inf.read())
buf = zlib.decompress(buf) # 查看这个代码文件能发现密文是用zlib压缩过的所以需要解压缩
out = open(r'PYZ-00.pyz_extracted\baby_python\baby_core.pyc', 'wb')
out.write(buf)
print('written down %d bytes' % len(buf))
inf.close()
out.close()
然后运行,得到解密后的 pyc。
参考 struct.pyc
头部16bytes,给这个 baby_core.pyc
补上 magic number 等信息。
然后反编译这个 pyc,得到 py 源代码。
uncompyle6 baby_core.pyc > baby_core.py
# uncompyle6 version 3.7.4
# Python bytecode 3.7 (3394)
# Decompiled from: Python 3.7.3 (default, Mar 27 2019, 17:13:21) [MSC v.1915 64 bit (AMD64)]
# Embedded file name: baby_python\baby_core.py
# Compiled at: 1995-09-28 00:18:56
# Size of source mod 2**32: 272 bytes
import hashlib
def md5(s: bytes) -> str:
m = hashlib.md5()
m.update(s)
return m.hexdigest().lower()
def main():
secret = input('secret: ')
if len(secret) != 48:
return
else:
return secret.isnumeric() or None
values = []
for i in range(0, 48, 3):
values.append(int(secret[i:i + 3]))
co = [[158, 195, 205, 229, 213, 238, 211, 198, 190, 226, 135, 119, 145, 205, 113, 122],
[
234, 256, 185, 253, 244, 134, 102, 117, 190, 106, 131, 205, 198, 234, 162, 218],
[
164, 164, 209, 200, 168, 226, 189, 151, 253, 241, 232, 151, 193, 119, 226, 193],
[
213, 117, 151, 103, 249, 148, 103, 213, 218, 222, 104, 228, 100, 206, 218, 177],
[
217, 202, 126, 214, 195, 125, 144, 105, 152, 118, 167, 137, 171, 173, 206, 240],
[
160, 134, 131, 135, 186, 213, 146, 129, 125, 139, 174, 205, 177, 240, 194, 181],
[
183, 213, 127, 136, 136, 209, 199, 191, 150, 218, 160, 111, 191, 226, 154, 191],
[
247, 188, 210, 219, 179, 204, 155, 220, 215, 127, 225, 214, 195, 162, 214, 239],
[
108, 112, 104, 133, 178, 138, 110, 176, 232, 124, 193, 239, 131, 138, 161, 218],
[
140, 213, 142, 181, 179, 173, 203, 208, 184, 129, 129, 119, 122, 152, 186, 124],
[
105, 205, 124, 142, 175, 184, 234, 119, 195, 218, 141, 122, 202, 202, 190, 178],
[
183, 178, 256, 124, 241, 132, 163, 209, 204, 104, 175, 211, 196, 136, 158, 210],
[
224, 144, 189, 106, 177, 251, 206, 163, 167, 144, 208, 254, 117, 253, 100, 106],
[
251, 251, 136, 170, 145, 177, 175, 124, 193, 188, 193, 198, 208, 171, 151, 230],
[
143, 200, 143, 150, 243, 148, 136, 213, 161, 224, 170, 208, 185, 117, 189, 242],
[
234, 188, 226, 194, 248, 168, 250, 244, 166, 106, 113, 218, 209, 220, 158, 228]]
r = [
472214, 480121, 506256, 449505, 433390, 435414, 453899, 536361, 423332, 427624, 440268, 488759, 469049, 484574,
480266, 522818]
for i in range(16):
v = 0
for j in range(16):
v += co[i][j] * values[j]
if v != r[i]:
return
print('flag{ISEC-%s}' % md5(secret.encode()))
# okay decompiling baby_core.pyc
可以看出,输入的数字为48位,三位三位将转成 values
数组,做了个矩阵求解,判断结果是否与 r
相符。
那其实就是要求这个矩阵的系数 values
了,最后拿 z3 solver 一把梭 就完事了~
from z3 import *
import hashlib
def md5(s: bytes) -> str:
m = hashlib.md5()
m.update(s)
return m.hexdigest().lower()
co = [[158, 195, 205, 229, 213, 238, 211, 198, 190, 226, 135, 119, 145, 205, 113, 122],
[
234, 256, 185, 253, 244, 134, 102, 117, 190, 106, 131, 205, 198, 234, 162, 218],
[
164, 164, 209, 200, 168, 226, 189, 151, 253, 241, 232, 151, 193, 119, 226, 193],
[
213, 117, 151, 103, 249, 148, 103, 213, 218, 222, 104, 228, 100, 206, 218, 177],
[
217, 202, 126, 214, 195, 125, 144, 105, 152, 118, 167, 137, 171, 173, 206, 240],
[
160, 134, 131, 135, 186, 213, 146, 129, 125, 139, 174, 205, 177, 240, 194, 181],
[
183, 213, 127, 136, 136, 209, 199, 191, 150, 218, 160, 111, 191, 226, 154, 191],
[
247, 188, 210, 219, 179, 204, 155, 220, 215, 127, 225, 214, 195, 162, 214, 239],
[
108, 112, 104, 133, 178, 138, 110, 176, 232, 124, 193, 239, 131, 138, 161, 218],
[
140, 213, 142, 181, 179, 173, 203, 208, 184, 129, 129, 119, 122, 152, 186, 124],
[
105, 205, 124, 142, 175, 184, 234, 119, 195, 218, 141, 122, 202, 202, 190, 178],
[
183, 178, 256, 124, 241, 132, 163, 209, 204, 104, 175, 211, 196, 136, 158, 210],
[
224, 144, 189, 106, 177, 251, 206, 163, 167, 144, 208, 254, 117, 253, 100, 106],
[
251, 251, 136, 170, 145, 177, 175, 124, 193, 188, 193, 198, 208, 171, 151, 230],
[
143, 200, 143, 150, 243, 148, 136, 213, 161, 224, 170, 208, 185, 117, 189, 242],
[
234, 188, 226, 194, 248, 168, 250, 244, 166, 106, 113, 218, 209, 220, 158, 228]]
r = [
472214, 480121, 506256, 449505, 433390, 435414, 453899, 536361, 423332, 427624, 440268, 488759, 469049, 484574,
480266, 522818]
s = Solver()
values = [Int('v%d' % i) for i in range(16)]
for i in range(16):
v = 0
for j in range(16):
v += co[i][j] * values[j]
s.add(v == r[i])
for j in range(16):
s.add(values[j] > 0)
s.check()
answer = s.model()
print(answer)
# [v13 = 103, v9 = 109, v12 = 152, v14 = 124, v10 = 244, v2 = 188, v0 = 113, v3 = 123, v15 = 165, v6 = 154, v7 = 241, v4 = 164, v8 = 163, v11 = 215, v1 = 201, v5 = 176]
result = "".join([str(answer[each]) for each in values])
print(result)
# 113201188123164176154241163109244215152103124165
len(result)
# 48
print('flag{ISEC-%s}' % md5(result.encode()))
# flag{ISEC-ca32ab6174689b5e366241ad58108c68}
Extensive Reading:
反编译Python打包的可执行程序 这个用的
archive_viewer.py
python逆向实战:反编译pyinstaller打包生成的exe
下面是 Z3 相关,喵喵之前写的
Crypto
Crypto2
又是一堆编码套娃……
开局是 十六进制编码
486d65656d727720516372697a716e7a72707a687271207262205a6278656d7163206e767a612072626b206e65727468706d7863615b32362c34352c31362c35362c31375d2c20686d6b7a657420707a7872706b7a6b2072712061637a2078707a72617a71612068706d617a70206d622061637a205a6278656d716320657262787372787a2072626b2061637a20687670656b2771206e707a2d7a776d627a6261206b70727772616d71612e437a206d71207675617a62206a7265657a6b205a62786572626b2771206272616d76627265206e767a612072626b2061637a20224472706b2076752052677662225b382c32302c33382c31322c37322c34322c332c365d2e436d7120717370676d676d62782068767069712c206d626a65736b6d6278207176777a206a7665657264767072616d7662712c206a7662716d7161207675207264767361203338206e657274712c717662627a61712c206168762065766278206272707072616d677a206e767a77712c2072626b20717a677a707265207661637a70206e767a77712e20436d71206e65727471206372677a20647a7a622061707262716572617a6b206d626176207a677a70742077726c767020656d676d627820657262787372787a2072626b2072707a206e7a70757670777a6b207776707a207675617a62206163726220616376717a20767520726274207661637a70206e65727468706d7863615b31302c36322c31322c35392c332c33382c35312c34352c342c31342c34312c31335d2e0a516372697a716e7a72707a2068727120647670622072626b2070726d717a6b206d622051617072617576706b2d736e76622d526776622e2041637a20697a74206d712076622061636d7120716172787a2e2052612061637a2072787a2076752031382c20637a20777270706d7a6b205262627a2043726163726872742c20686d6163206863767720637a2063726b206163707a7a206a636d656b707a623a20517371726262722c2072626b2061686d627120437277627a612072626b204c736b6d61632e20447a61687a7a6220313538352072626b20313539322c20637a20647a78726220722071736a6a7a7171757365206a72707a7a705b34312c34332c32322c35342c31322c34322c33332c35312c385d206d62204576626b766220727120726220726a6176702c2068706d617a702c2072626b206e727061207668627a702076752072206e6572746d6278206a76776e726274206a7265657a6b2061637a204576706b204a637277647a7065726d62277120577a622c206572617a702069627668622072712061637a20496d6278277120577a622e2041637a20756572782074767320627a7a6b206d7120637a707a3a615474486c545369587a4961677a7351596970684d446467635566616d765a554a444c634d7441427672553d3d2e20437a20726e6e7a727071206176206372677a20707a616d707a6b2061762051617072617576706b2072707673626b20313631332c2068637a707a20637a206b6d7a6b206163707a7a20747a727071206572617a702e20557a6820707a6a76706b7120767520516372697a716e7a72707a2771206e706d6772617a20656d757a20717370676d677a2c2072626b2061637a707a2063727120647a7a62206a7662716d6b7a707264657a20716e7a6a736572616d76622072647673612071736a6320777261617a707120727120636d71206e6374716d6a726520726e6e7a727072626a7a2c20717a667372656d61742c20707a656d786d76737120647a656d7a75712c2072626b2068637a61637a702061637a20687670697120726161706d6473617a6b20617620636d7720687a707a2068706d61617a62206474207661637a70715b31362c34332c33312c332c35342c31322c33332c31352c35395d2e
转 ASCII
Hmeemrw Qcrizqnzrpzhrq rb Zbxemqc nvza rbk nerthpmxca[26,45,16,56,17], hmkzet pzxrpkzk rq acz xpzrazqa hpmazp mb acz Zbxemqc erbxsrxz rbk acz hvpek'q npz-zwmbzba kprwramqa.Cz mq vuazb jreezk Zbxerbk'q bramvbre nvza rbk acz "Drpk vu Rgvb"[8,20,38,12,72,42,3,6].Cmq qspgmgmbx hvpiq, mbjeskmbx qvwz jveerdvpramvbq, jvbqmqa vu rdvsa 38 nertq,qvbbzaq, ahv evbx brppramgz nvzwq, rbk qzgzpre vaczp nvzwq. Cmq nertq crgz dzzb aprbqerazk mbav zgzpt wrlvp emgmbx erbxsrxz rbk rpz nzpuvpwzk wvpz vuazb acrb acvqz vu rbt vaczp nerthpmxca[10,62,12,59,3,38,51,45,4,14,41,13].
Qcrizqnzrpz hrq dvpb rbk prmqzk mb Qaprauvpk-snvb-Rgvb. Acz izt mq vb acmq qarxz. Ra acz rxz vu 18, cz wrppmzk Rbbz Cracrhrt, hmac hcvw cz crk acpzz jcmekpzb: Qsqrbbr, rbk ahmbq Crwbza rbk Lskmac. Dzahzzb 1585 rbk 1592, cz dzxrb r qsjjzqquse jrpzzp[41,43,22,54,12,42,33,51,8] mb Evbkvb rq rb rjavp, hpmazp, rbk nrpa vhbzp vu r nertmbx jvwnrbt jreezk acz Evpk Jcrwdzpermb'q Wzb, erazp ibvhb rq acz Imbx'q Wzb. Acz uerx tvs bzzk mq czpz:aTtHlTSiXzIagzsQYiphMDdgcUfamvZUJDLcMtABvrU==. Cz rnnzrpq av crgz pzampzk av Qaprauvpk rpvsbk 1613, hczpz cz kmzk acpzz tzrpq erazp. Uzh pzjvpkq vu Qcrizqnzrpz'q npmgraz emuz qspgmgz, rbk aczpz crq dzzb jvbqmkzprdez qnzjseramvb rdvsa qsjc wraazpq rq cmq nctqmjre rnnzrprbjz, qzfsremat, pzemxmvsq dzemzuq, rbk hczaczp acz hvpiq raapmdsazk av cmw hzpz hpmaazb dt vaczpq[16,43,31,3,54,12,33,15,59].
然后拿到 quipquip 一把梭
得到这个,发现其实就是 维基百科那个改的。
William Shakespearewas an English poet and playwright[26,45,16,56,17], widely regarded as the greatest writer in the English language and the world's pre-eminent dramatist.He is often called England's national poet and the "Bard of Avon"[8,20,38,12,72,42,3,6].His surviving works, including some collaborations, consist of about 38 plays,sonnets, two long narrative poems, and several other poems. His plays have been translated into every major living language and are performed more often than those of any other playwright[10,62,12,59,3,38,51,45,4,14,41,13]. Shakespeare was born and raised in Stratford-upon-Avon. The key is on this stage. At the age of 18, he married Anne Hathaway, with whom he had three children: Susanna, and twins Hamnet and Judith. Between 1585 and 1592, he began a successful career[41,43,22,54,12,42,33,51,8] in London as an actor, writer, and part owner of a playing company called the Lord Chamberlain's Men, later known as the King's Men. The flag you need is here:tYyWjYUkGeKtveuSZkrwIBbvhFxtioEFCBJhIyTNoaF==. He appears to have retired to Stratford around 1613, where he died three years later. Few records of Shakespeare's private life survive, and there has been considerable speculation about such matters as his physical appearance, sexuality, religious beliefs, and whether the works attributed to him were written by others[16,43,31,3,54,12,33,15,59].
这串说的 flag 好像 base64 啊,然而发现偏移都不对。
又发现去掉 ==
之后长度为 43,而这些数组合起来的元素个数也是 43,寻思着存在某种映射。但是一想最大值 72,不可能是改 base64 表了。
摸不着头脑.jpg
后来队友想了个半天也没想出来,摸了。
赛后才知道,是 逐位异或 啊,草了啊啊啊啊啊啊!喵呜呜。
Exp:
s = "tYyWjYUkGeKtveuSZkrwIBbvhFxtioEFCBJhIyTNoaF"
l = [26,45,16,56,17,8,20,38,12,72,42,3,6,10,62,12,59,3,38,51,45,4,14,41,13,41,43,22,54,12,42,33,51,8,16,43,31,3,54,12,33,15,59]
r = ''
for k, v in enumerate(s):
r += chr(ord(v) ^ l[k])
print(r)
# ntio{QAMK-awpoK_ahTDdFl_eoSb_cogpJZCVzbBNn}
接着对每个字母循环移位一下就完事了。(经典凯撒
flag{ISEC-sohgC_szLVvXd_wgKt_ugyhBRUNrtTFf}
小结
摸了,这个比赛的前一天在打强网拟态防御那个比赛,不说别的,被 sb Misc 题目的 sb 出题人折磨了一晚上,难受死了。(你搁着和咱玩拟态呢
然后寻思着群友都在看 hackergame,就来瞄了眼,水了几题到了第37,于是天亮了。
睡一觉起来发现队友也摸了,那就佛系看这个比赛了……
于是乎一个队友全程摸了,另一个队友解了半天 crypto 都没解出来,佛了。
然而赛后其他师傅说前两个 crypto 都是原题。。
这次倒是第一次看这种加密的 pyc 逆向,感觉还是有点意思的。
Misc 剩下的三道题全是 apk,要逆向再解,佛了。
还看了个 web,然而喵喵没打通。这是之前一个比赛的原题改的,第一步是个 SSRF,噢,可以参考 [LCTF]bestphp’s revenge,或者 LCTF2018-bestphp's revenge 详细题解。只不过把内层的 flag.php
改成了从本地访问的木马,且过滤了字母数字,拿无字母数字 webshell 打一下就行。
赛后其他师傅说这题靶机没有 bash nc 这些,可以通过构造管道把 shell 弹出来,最后读 phpinfo 之类的好像。(环境关了也复现不了了
总之吧,题目还是有点难度的,不过可能是这周比赛太多了,打的师傅不算多。咱也就摸摸鱼看看题罢了。
就这样吧。
(溜了溜了喵