刚出新手村后正经打的第一场个人赛,单就re方向来说感觉难度还行?
Reverse方向:
babyre:
反编译观察主函数:
1 | __int64 __fastcall main(int a1, char **a2, char **a3) |
查看其中的sub_147E和sub_1558:
1 | void sub_147E() |
这两个函数仅仅展示了判断奇偶性和随机生成布尔值的算法,再看sub_13A9:
1 | unsigned __int64 __fastcall sub_13A9(__int64 a1, __int64 a2) |
发现是一个简单的异或,但实际运行却由于未知原因会产生段错误,查看v8的来源函数sub_1DC2:
1 | _QWORD *__fastcall sub_1DC2(_QWORD *a1, _QWORD *a2) |
只是比较了两个数组的长度大小,取两个之中更短的那个长度,查看main后续的sub_1920:
1 | unsigned __int64 __fastcall sub_1920(__int64 a1, __int64 a2) |
发现是base64加密,查看有没有换表,查看dword_2040发现是yzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/abcdefghijklmnopqrstuvwx,说明换表了,接着往后查看sub_17A7:
1 | unsigned __int64 __fastcall sub_17A7(__int64 a1) |
发现是比较结果,查看dword_4020:[0x51, 0x43, 0x54, 0x43, 0x55, 0x42, 0x5A, 0x76, 0x4F, 0x46, 0x48, 0x73, 0x5C, 0x46, 0x7D, 0x6B, 0x4E, 0x50, 0x55, 0x68, 0x51, 0x55, 0x7D, 0x3E, 0x45, 0x5D, 0x43, 0x67, 0x45, 0x3E, 0x3B, 0x3D, 0x47, 0x49, 0x53, 0x20, 0x54, 0x59, 0x43, 0x60, 0x40, 0x5F, 0x49, 0x7E, 0x45, 0x38, 0x75, 0x38, 0x47, 0x7C, 0x25, 0x29, 0x5A, 0x7D, 0x59, 0x63, 0x5F, 0x46, 0x57, 0x38, 0x5F, 0x42, 0x79, 0x28],至此已经分析完毕,输入的字符串先与一个key进行异或(疑似会报错),接着经过了换表的base64,再之后又异或了索引号整除以3的值,最后与加密的flag进行比较,先把最后的异或索引号逆向:
1 | enc = [0x51, 0x43, 0x54, 0x43, 0x55, 0x42, 0x5A, 0x76, 0x4F, 0x46, 0x48, 0x73, 0x5C, 0x46, 0x7D, 0x6B, 0x4E, 0x50, 0x55, 0x68, 0x51, 0x55, 0x7D, 0x3E, 0x45, 0x5D, 0x43, 0x67, 0x45, 0x3E, 0x3B, 0x3D, 0x47, 0x49, 0x53, 0x20, 0x54, 0x59, 0x43, 0x60, 0x40, 0x5F, 0x49, 0x7E, 0x45, 0x38, 0x75, 0x38, 0x47, 0x7C, 0x25, 0x29, 0x5A, 0x7D, 0x59, 0x63, 0x5F, 0x46, 0x57, 0x38, 0x5F, 0x42, 0x79, 0x28] |
输出0x51 0x43 0x54 0x42 0x54 0x43 0x58 0x74 0x4d 0x45 0x4b 0x70 0x58 0x42 0x79 0x6e 0x4b 0x55 0x53 0x6e 0x57 0x52 0x7a 0x39 0x4d 0x55 0x4b 0x6e 0x4c 0x37 0x31 0x37 0x4d 0x42 0x58 0x2b 0x58 0x55 0x4f 0x6d 0x4d 0x52 0x47 0x70 0x4b 0x37 0x7a 0x37 0x57 0x6c 0x35 0x38 0x4b 0x6c 0x4b 0x71 0x4d 0x55 0x44 0x2b 0x4b 0x56 0x6d 0x3d,转换得到换表后的base64:QCTBTCXtMEKpXBynKUSnWRz9MUKnL717MBX+XUOmMRGpK7z7Wl58KlKqMUD+KVm=,带入cyberchef解密直接得到flag了:HECTF{8c7d051e5a0e9c567c86fed492720cc8d3389af1}.
littleasm:
观察汇编代码:
1 | .data |
编写解密脚本:
1 | enc_flag = [0x6a, 0x28, 0x3d, 0x4e, 0x2b, 0x05, 0x63, 0x1e, 0x0d, 0x73, 0x10, 0x1c, 0x73, 0x24, 0x21, 0x73, 0x5e, 0x21, 0x31, 0x5d, 0x21, 0x3f, 0x0c, 0x0d, 0x6d, 0x4c, 0x03] |
运行得到flag:HECTF{Ass1mb1y_13_s0_eas7!}.
PE?py?:
查看压缩包发现需要密码,pyre联系名称是python逆向,用pyinstxtractor提取后再用pycdc反编译pyre.pyc,得到:
1 | print('PE?py?\n') |
则压缩包密码就是“Have_a_cup_of_tea_together”,解压缩得到main1.exe
,发现就是正常的程序,直接用ida反编译,查看main:
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
发现结构很清晰,再查看加密函数:
1 | void __cdecl encrypt(unsigned int num_rounds, uint32_t *v, const uint32_t *key) |
发现是XTEA,查看数组得到key为[0x48, 0x45, 0x43, 0x54, 0x46, 0x32, 0x30, 0x32, 0x34, 0x77, 0x61, 0x69, 0x74, 0x79, 0x6F, 0x75](HECTF2024waityou),转换为32位整数得到[0x54434548, 0x32303246, 0x69617734, 0x756F7974],而加密的flag则是[0x94, 0x61, 0x59, 0xDF, 0xD6, 0x74, 0xFE, 0x2C, 0x4D, 0xAE, 0x55, 0x13, 0x87, 0x7A, 0x71, 0xB6, 0x7B, 0xB5, 0x7B, 0xF2, 0xC5, 0x36, 0xD4, 0x08, 0xAF, 0xE1, 0xC8, 0xC5, 0x8F, 0xBD, 0x85, 0x0A, 0x32, 0x00, 0xA7, 0x19, 0xF4, 0xFE, 0x0C, 0x40, 0xFC, 0xE1, 0x02, 0xAF, 0xB4, 0xCF, 0xED, 0xCD](48长度,带入XTEA加密,4个字符一组是[0xDF596194, 0x2CFE74D6, 0x1355AE4D, 0xB6717A87, 0xF27BB57B, 0x08D436C5, 0xC5C8E1AF, 0x0A85BD8F, 0x19A70032, 0x400CFEF4, 0xAF02E1FC, 0xCDEDCFB4]),基本信息已经获得完全,还原加密函数:
1 | m1 = 0x34333231 # 小端序前四字节 |
带入检验发现输出结果相同,编写逆向解密:
1 | # 密文 |
输出:0x54434548 0x38357b46 0x31306564 0x612d6366 0x2d623666 0x36666338 0x6338652d 0x62642d61 0x34363935 0x62306563 0x7d6531 0x0,十六进制转字符串得TCEH 85{F 10ed a-cf -b6f 6fc8 c8e- bd-a 4695 b0ec }e1
,转换端序得到flag:HECTF{58de01fc-af6b-8cf6-e8ca-db5964ce0b1e}.
ezAndroid:
题目提示说该APK不能正常运行,那么只能静态分析了,查看MainActivity
,发现了引用的本地库ezandroid
,还发现了key字符串为“HECTF”,接下来给出了一个RC4解密函数,然后用RC4和key解密了一个名为enc.png
的文件,尝试写入/sdcard
的right.txt
,此外还发现了一个CheckActivity
,点击按钮时执行下面的代码:
1 | public void onClick(View view) { |
那么关键的加密应该都在so层,而此外CheckActivity
还给出了一个func
函数,会将16长度的数组与“hectf2024”循环异或,但貌似没找到引用,至此线索分成两个方向,一个是enc.png,另一个是so文件,提取资源文件,发现enc.png有7.3M,先将enc.png试图进行RC4解密,得到了right.txt,发现其实是png文件,查看得到flag格式是HECTF{填入数据的MD5},接下来查看so文件,找到两个函数并反编译:
1 | unsigned __int64 __fastcall Java_com_hectf2024_ezandroid_CheckActivity_d0func(__int64 a1) |
貌似是把这两个数组带入了func函数进行了加密,异或后的结果为:
1 | Xor5000 = [0x30, 0x77, 0x79, 0x78, 0x7d, 0x6c, 0x68, 0x72, 0x72, 0x6e, 0x6f, 0x45, 0x30, 0x6a, 0x64, 0x78] |
但并不清楚哪个是flag,再看另一个函数:
1 | __int64 __fastcall Java_com_hectf2024_ezandroid_CheckActivity_stringFromJNI(__int64 a1, __int64 a2, __int64 a3) |
其中sub_1028查看后内部是一个标准的TEA加密,只会返回1且带入的两个数全部相同:
1 | __int64 __fastcall sub_1028(const char *a1) |
1 | __int64 __fastcall sub_1140(unsigned int *a1, _DWORD *a2) |
但没找到这些加密与上述两个数组之间的关系,再仔细观察,发现sub_2090有明显的ollvm混淆,用d810去混淆后,发现会检测sub_1028并将其替换为sub_1E20,也就是之前的sub_1028实际上是假的,真正的加密逻辑是sub_1E20,观察sub_1E20发现还是很混乱,再次去除ollvm混淆得到:
1 | __int64 __fastcall sub_1E20(__int64 a1) |
观察sub_1220:
1 | __int64 sub_1220() |
发现是把256项数组byte_50C0异或0x18后存入byte_52D0,猜测可能是s盒之类的东西,提取出来数据为:
1 | byte_50C0 = [0x7B, 0x64, 0x6F, 0x63, 0x0E, 0x73, 0x77, 0xDD, 0x28, 0x19, 0x7F, 0x33, 0xE6, 0xCF, 0xB3, 0x6E, |
则byte52d0为:
1 | byte_52d0 = [0x63, 0x7c, 0x77, 0x7b, 0x16, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, |
发现是AES的S盒,再观察sub_1C90:
1 | unsigned __int64 __fastcall sub_1C90(_BYTE *a1, __int64 a2, __int64 a3) |
再看sub_1A30主要逻辑:
1 | __memcpy_chk(v6, a1, 16LL, 16LL); |
发现就是AES_CBC加密,则总的加密逻辑是(sub_1A30转换成的python代码附在下面):
Dword5000—func—XOR hectf2024→X5000(key)
Dword5040—func—XOR hectf2024→X5040(iv)
Byte50D0—sub1220—51D0—XOR 0x18→52D0(S盒)
AES_CBC(input,X5000,X5040)→dword_5080(密文)
先从要比较的dword_5080提取得到[0xC6, 0x9B, 0x16, 0x0F, 0x67, 0x03, 0x68, 0xB9, 0x3F, 0xD7, 0xE9, 0x16, 0x0E, 0x97, 0x63, 0xBB],用X5000和X5040AES_CBC解得明文十六进制为d8ac2d59f876b54a554242e4e6d32078,计算32位小写md5带入检验发现不对,再仔细检测so文件发现还有三个函数,其中一个对dword_5000每个数的值加上了1,另一个对dword_5040每个数的值先加上了2又异或了0x18,再次代入,得到xor5000和xor5040的真正值:
1 | Xor5000:0x31 0x76 0x78 0x79 0x7a 0x6d 0x69 0x73 0x73 0x6f 0x6e 0x44 0x31 0x6b 0x65 0x79(1vxyzmissonD1key) |
AES_CBC解密得到明文“adHIHFBDCJsoiak”,计算md5得到cfa76d5f2d5aecdb53e79f644cb90fc2即为flag.
easyree:
拖入exeinfope发现是upx,但被篡改了,010editor打开发现UPX头(UPX012、UPX!)被替换成了CTF,修改后用UPX脱壳成功,查看main函数:
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
发现data是被加密的flag的中间态,提取得到[0x23, 0x21, 0x20, 0x28, 0x25, 0x7E, 0x28, 0x46, 0x52, 0x04, 0x4B, 0x52, 0x4C, 0x03, 0x52, 0x04, 0x48, 0x4F, 0x7B, 0x4F, 0x7D, 0x42, 0x44, 0x04, 0x4F, 0x49, 0x70],观察加密函数encrypt:
1 | void __cdecl encrypt(char *a, int tot) //起始tot = 8 |
发现是一个简单的变位凯撒密码,在下方还有一个小的加密,将每项的ascii减去21再与data每项与k异或得到的值进行比较, 由于flag格式是HECTF{,第六个字符不参与变位凯撒加密,所以k其实可以计算出来,有123({)-21 = 102(0x66) = 126(0x7E)^k
,解得k=0x18(24),同理带入最后一个字符125(})-21 = 104(0x68) = 112(0x70)^0x18,验证通过,则data^k = encflag-21 = [0x3b, 0x39, 0x38, 0x30, 0x3d, 0x66, 0x30, 0x5e, 0x4a, 0x1c, 0x53, 0x4a, 0x54, 0x1b, 0x4a, 0x1c, 0x50, 0x57, 0x63, 0x57, 0x65, 0x5a, 0x5c, 0x1c, 0x57, 0x51, 0x68],得到encflag = [0x50, 0x4e, 0x4d, 0x45, 0x52, 0x7b, 0x45, 0x73, 0x5f, 0x31, 0x68, 0x5f, 0x69, 0x30, 0x5f, 0x31, 0x65, 0x6c, 0x78, 0x6c, 0x7a, 0x6f, 0x71, 0x31, 0x6c, 0x66, 0x7d],转换得PNMER{Es_1h_i0_1elxlzoq1lf},代换回凯撒加密之前得到HECTF{Re_1s_s0_1nterest1ng}.
附解密脚本:
1 | def encrypt(s, tot=8): |
Pwn方向:
sign in:
观察程序主函数,要先输入用户名和密码,用户名不能超过8个字符,密码要与m(“HECTF2024!”)相等,之后就可以进入backd00r函数并拿到shell,但在拿到之前程序关闭了输出(close(1)),因此拿到shell之后将flag内容重定向到标准错误(输入cat /flag >&2),得到flag:HECTF{01a86b7bc0e0e58d25ea10edccedfc2762f8e3fd}.
Crypto方向:
迷茫的艾米莉:
题目提示了维尼吉亚密码
和栅栏密码
,由于维尼吉亚密码不会改变大括号的位置,所以先用栅栏密码,将原密码Y2w9Iobe_v_Ufbm0ajI05bfzvTP1b_c}{lr带入cyberchef选择Rail Fence,栏数选6得到初步解密结果YIUIT{P0fo2bb51lbbmew_0f_rczav9_jv},再用维尼吉亚密码解密,带入key=responsibility得到flag:HECTF{C0ng2at51ations_0n_comin9_in}.
seven more:
发现e与p-1,q-1均不互质,无法求得模逆元d,又观察发现p-1是e的整数倍,q-1与e共因数1009,考虑使用AMM算法得到原信息,建立脚本:
1 | from Crypto.Util.number import * |
运行得到结果:
1 | b'HECTF{go0d_jOb_At_AmM}D~u3<1\xdd\x9f\x81b:,\xbe\xf2\x1c\xf1\xd5\xeeN\xb7w\xce\xae?\xf3~\x99\xbd\xce\xf1\xf1\x10"\xc6:\x85\x08\xf0\xef4h\x8c%d\x9f\xf1\xaf\xfdS\xd1\xcc\x99\x1c\xc3\xe5\x06Z\xdags\xb5R>\xc9\xd4\xb7\x99\xde\x9f\xb0\xccP\xf2\xba\x82A\xfd\xbb\xda\x0e\xf9\xa5\xaa\xfcm\x92\xba\xa8\xff\xf8*\x8a\x95p\xb6\xbe\xc5\xbf\xbe\xe8c' |
前面的即为flag:HECTF{go0d_jOb_At_AmM}.
翻一翻:
发现是对称质数emirp,找到2015年ASIS总决赛的RSASR的wp脚本作为模板,更改脚本,爆破pq:
1 | n = 404647938065363927581436797059920217726808592032894907516792959730610309231807721432452916075249512425255272010683662156287639951458857927130814934886426437345595825614662468173297926187946521587383884561536234303887166938763945988155320294755695229129209227291017751192918550531251138235455644646249817136993 |
得到:
1 | p=39316409865082827891559777929907275271727781922450971403181273772573121561800306699150395758615464222134092274991810028405823897933152302724628919678029201 |
解得明文SEVDVEZ7SV9yZWExbHlfbDB2ZV9jMnlwdG8hfQ==,base64解密得到HECTF{I_rea1ly_l0ve_c2ypto!}.
Misc方向:
funny:
打开第一张图片看到广告为JK FUN,高德地图查询只有一个结果,在北京市西城区,查看得到所在广场为“西外文化休闲广场”,再查看周围的水系,找到“京城水系慈禧水道”,查看图片发现就是这个地方,填入flag:HECTF{北京市-西城区-西外文化休闲广场-京城水系慈禧水道}发现正确.
简单的压缩包:
查看Re.txt发现给出了压缩包密码的正则表达式:^([a-z]){2}\d([^a-z])\D$
,说明密码长度为5,且前两个字符为小写字母,第三个字符为数字,第四个字符不是小写字母,第五个字符不是数字,按要求生成字典文件:
1 | import re |
生成后用Archpr爆破,得到密码:np76_,解开压缩包,提取出来图片,猜测有copy/B隐写,直接改后缀为rar,发现果然有,里面有一个zip.zip和一个getzip.py,查看发现zip以key = b”abcdefghijklmnop”和iv = b”qwertyuiopasdfgh”进行了加密,AES_CBC解密后打开被解密的zip,发现一个文件夹,里面就是flag.txt,得到flag:HECTF{c292af1-2b2ee35-6398bd4934f7626afc}.
附:python解密脚本:
1 | from Crypto.Cipher import AES |
Rem_YOU:
下载发现是png,但是010发现是jpg+压缩包,改后缀名为rar,打开发现有一个文件夹,里面是9个二维码的切片,ps合并扫描得到JBCUGVCGPN2VMM3YPBRTOUZYNF4UETSUPB2GM6DWKBZE6N2SIZCTGZ2MOBZG6OLBGNAVOMSLKB6Q====,是flag的base32,解密得到HECTF{uV3xxc7S8iyBNTxtfxvPrO7RFE3gLpro9a3AW2KP}.