给了一个 exe,需要逆向,得到的源代码大概如下:
undefined8 main(void)
{
size_t sVar1;
FUN_00100b0a();
puts("Hello there , i lost my dog pluto :(\nHelp me call him out please !!");
__isoc99_scanf(&%s,0x302100);
sVar1 = strlen(buffer3 + 0x20);
if (sVar1 != 0x1e) {
FUN_001008aa();
}
buffer2[0] = (int)buffer3[33] + buffer3[32] + -0x32;
buffer2[1] = (int)buffer3[34] + buffer3[33] + -100;
buffer2[2] = (int)buffer3[34] << 2;
buffer2[3] = SEXT14((char)(buffer3[35] ^ 0x46));
buffer2[4] = 0x24 - ((int)(char)buffer3[35] - (int)buffer3[36]);
buffer2[6] = (int)buffer3[37] * (int)(char)buffer3[38] + 99;
buffer2[7] = SEXT14((char)(buffer3[39] ^ buffer3[38]));
buffer2[8] = (int)buffer3[40] ^ (int)(char)buffer3[39] + 0x2dU;
buffer2[9] = ((int)buffer3[41] & 0x37U) - 3;
buffer2[11] = (int)buffer3[43] - 0x26;
buffer2[12] = ((char)(buffer3[38] ^ buffer3[44]) + 4) * 4;
buffer2[5] = (int)buffer3[53] - (int)buffer3[36] ^ 0x30;
buffer2[13] = ((int)buffer3[45] - (int)buffer3[46]) - 1;
buffer2[10] = ((int)buffer3[49] - (int)buffer3[48]) + 0x52;
buffer2[16] = (char)(buffer3[51] ^ buffer3[50]) * 6 + 0x36;
buffer2[17] = (int)(char)(buffer3[52] ^ 0x73) + buffer3[53] + 0x31;
buffer2[14] = SEXT14(buffer3[54]);
buffer2[18] = SEXT14((char)(buffer3[55] ^ 0x42));
buffer2[15] = (int)buffer3[58] + 5;
buffer2[19] = ((int)buffer3[57] - (int)(buffer3[58] / '\x02')) - 0x37;
buffer2[20] = buffer3[59] * 4 - (buffer3[60] + 0x80);
buffer2[21] = (int)buffer3[61] - 0x20;
FUN_001008d0(buffer2);
return 0;
}
void FUN_001008d0(undefined4 *param_1)
{
char cVar1;
int iVar2;
buffer3[0] = ((byte)*param_1 ^ 2) - 0x1f;
cVar1 = (char)((int)param_1[1] >> 0x1f);
buffer3[1] = ((byte)*param_1 ^ ((char)param_1[1] - cVar1 & 1U) + cVar1) - 0x1d;
buffer3[2] = (byte)(param_1[1] << 2) ^ 0x97;
buffer3[4] = ((byte)param_1[3] ^ 0x4d) + 7;
buffer3[5] = (char)(param_1[5] << 2) + -1;
buffer3[3] = (char)param_1[4] + 't';
buffer3[6] = (char)param_1[6] + '\x15';
buffer3[7] = (char)param_1[7] + -0x14;
buffer3[8] = (byte)param_1[8] ^ 99;
buffer3[9] = (((byte)param_1[10] ^ 3) - (char)param_1[8]) + '6';
buffer3[10] = (byte)param_1[9] ^ 0x42;
buffer3[11] = (byte)param_1[0xc] ^ 0xb3;
buffer3[12] = (char)param_1[0xd] + 0x12U ^ 0x1a;
buffer3[13] = (char)param_1[0xe] + -7;
buffer3[15] = (byte)param_1[0x11] ^ 0xe5;
buffer3[16] = ((byte)param_1[0x12] & 0x36) + 0x35;
buffer3[14] = (byte)param_1[0x13] ^ 0x34;
buffer3[17] = (byte)param_1[0x14] ^ 0xfd;
buffer3[18] = (byte)((int)param_1[0x14] >> ((byte)param_1[0x15] & 0x1f)) ^ 0x1c;
iVar2 = strcmp(buffer3,s_inctf{U_Sur3_m4Te?}_00302010);
if (iVar2 == 0) {
puts(
"\n ................\n |w00ff w00ff!! |\n \'\'\'\'\'V\'\'\'\'\'\'\'\' \n \n .~````~. \n .,/ \\,. \n ( | (0 0) | )\n ( | ____ | )\n (_/| \\__/ |\\_)\n \\__/\\__/\n \'-..-\' \n"
);
puts("\nYeey you found him !!!!\n Grab your reward from nc!");
/* WARNING: Subroutine does not return */
exit(1);
}
FUN_001008aa();
return;
}
大意就是输入一段字符串,然后经过一系列计算以后,将结果与预期字符串比对。一种办法是用 z3 实现,但是这里格式转换什么的比较麻烦,我就用 fuzz 的方法求解:
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
typedef uint8_t byte;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
if (Size != 30)
return 0;
char *buffer = (char *)Data;
int buffer2[22] = {};
buffer2[0] = (int)buffer[1] + buffer[0] + -0x32;
buffer2[1] = (int)buffer[2] + buffer[1] + -100;
buffer2[2] = (int)buffer[2] << 2;
buffer2[3] = ((char)(buffer[3] ^ 0x46));
buffer2[4] = 0x24 - ((int)(char)buffer[3] - (int)buffer[4]);
buffer2[6] = (int)buffer[5] * (int)(char)buffer[6] + 99;
buffer2[7] = ((char)(buffer[7] ^ buffer[6]));
buffer2[8] = (int)buffer[8] ^ (int)(char)buffer[7] + 0x2dU;
buffer2[9] = ((int)buffer[9] & 0x37U) - 3;
buffer2[11] = (int)buffer[11] - 0x26;
buffer2[12] = ((char)(buffer[6] ^ buffer[12]) + 4) * 4;
buffer2[5] = (int)buffer[21] - (int)buffer[4] ^ 0x30;
buffer2[13] = ((int)buffer[13] - (int)buffer[14]) - 1;
buffer2[10] = ((int)buffer[17] - (int)buffer[16]) + 0x52;
buffer2[16] = (char)(buffer[19] ^ buffer[18]) * 6 + 0x36;
buffer2[17] = (int)(char)(buffer[20] ^ 0x73) + buffer[21] + 0x31;
buffer2[14] = (buffer[22]);
buffer2[18] = ((char)(buffer[23] ^ 0x42));
buffer2[15] = (int)buffer[26] + 5;
buffer2[19] = ((int)buffer[25] - (int)(buffer[26] / '\x02')) - 0x37;
buffer2[20] = buffer[27] * 4 - (buffer[28] + 0x80);
buffer2[21] = (int)buffer[29] - 0x20;
char cVar1;
int iVar2;
char buffer3[19] = {};
int *param_1 = buffer2;
buffer3[0] = ((byte)*param_1 ^ 2) - 0x1f;
cVar1 = (char)((int)param_1[1] >> 0x1f);
buffer3[1] =
((byte)*param_1 ^ ((char)param_1[1] - cVar1 & 1U) + cVar1) - 0x1d;
buffer3[2] = (byte)(param_1[1] << 2) ^ 0x97;
buffer3[4] = ((byte)param_1[3] ^ 0x4d) + 7;
buffer3[5] = (char)(param_1[5] << 2) + -1;
buffer3[3] = (char)param_1[4] + 't';
buffer3[6] = (char)param_1[6] + '\x15';
buffer3[7] = (char)param_1[7] + -0x14;
buffer3[8] = (byte)param_1[8] ^ 99;
buffer3[9] = (((byte)param_1[10] ^ 3) - (char)param_1[8]) + '6';
buffer3[10] = (byte)param_1[9] ^ 0x42;
buffer3[11] = (byte)param_1[0xc] ^ 0xb3;
buffer3[12] = (char)param_1[0xd] + 0x12U ^ 0x1a;
buffer3[13] = (char)param_1[0xe] + -7;
buffer3[15] = (byte)param_1[0x11] ^ 0xe5;
buffer3[16] = ((byte)param_1[0x12] & 0x36) + 0x35;
buffer3[14] = (byte)param_1[0x13] ^ 0x34;
buffer3[17] = (byte)param_1[0x14] ^ 0xfd;
buffer3[18] =
(byte)((int)param_1[0x14] >> ((byte)param_1[0x15] & 0x1f)) ^ 0x1c;
char target[] = "inctf{U_Sur3_m4Te?}";
if (buffer3[0] != target[0]) {
return 0;
}
if (buffer3[1] != target[1]) {
return 0;
}
if (buffer3[2] != target[2]) {
return 0;
}
if (buffer3[3] != target[3]) {
return 0;
}
if (buffer3[4] != target[4]) {
return 0;
}
if (buffer3[5] != target[5]) {
return 0;
}
if (buffer3[6] != target[6]) {
return 0;
}
if (buffer3[7] != target[7]) {
return 0;
}
if (buffer3[8] != target[8]) {
return 0;
}
if (buffer3[9] != target[9]) {
return 0;
}
if (buffer3[10] != target[10]) {
return 0;
}
if (buffer3[11] != target[11]) {
return 0;
}
if (buffer3[12] != target[12]) {
return 0;
}
if (buffer3[13] != target[13]) {
return 0;
}
if (buffer3[14] != target[14]) {
return 0;
}
if (buffer3[15] != target[15]) {
return 0;
}
if (buffer3[16] != target[16]) {
return 0;
}
if (buffer3[17] != target[17]) {
return 0;
}
if (buffer3[18] != target[18]) {
return 0;
}
__builtin_trap();
return iVar2;
}
拆分成很多分支是为了让 fuzzer 更容易通过走过的分支不同找到匹配更长前缀的输入,这里也许有更好的写法。然后编译运行:
$ clang++ -fsanitize=address,fuzzer fuzzer.cpp -o fuzzer
$ mkdir -p corpus
$ ./fuzzer -max_len=3 corpus/
NFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 3291888671
INFO: Loaded 1 modules (22 inline 8-bit counters): 22 [0x55afe826cc00, 0x55afe826cc16),
INFO: Loaded 1 PC tables (22 PCs): 22 [0x55afe826cc18,0x55afe826cd78),
INFO: A corpus is not provided, starting from an empty corpus
#2 INITED cov: 2 ft: 2 corp: 1/1b exec/s: 0 rss: 30Mb
#2947 NEW cov: 3 ft: 3 corp: 2/31b lim: 30 exec/s: 0 rss: 30Mb L: 30/30 MS: 5 CrossOver-EraseBytes-ChangeBinInt-ChangeByte-InsertRepeatedBytes-
#147578 NEW cov: 4 ft: 4 corp: 3/61b lim: 30 exec/s: 0 rss: 38Mb L: 30/30 MS: 1 InsertRepeatedBytes-
#148446 NEW cov: 5 ft: 5 corp: 4/91b lim: 30 exec/s: 0 rss: 38Mb L: 30/30 MS: 3 ChangeBinInt-CrossOver-ChangeByte-
#298952 NEW cov: 6 ft: 6 corp: 5/121b lim: 30 exec/s: 0 rss: 47Mb L: 30/30 MS: 1 ChangeByte-
#316988 NEW cov: 7 ft: 7 corp: 6/151b lim: 30 exec/s: 0 rss: 48Mb L: 30/30 MS: 1 ChangeByte-
#4194304 pulse cov: 7 ft: 7 corp: 6/151b lim: 30 exec/s: 1398101 rss: 276Mb
#8388608 pulse cov: 7 ft: 7 corp: 6/151b lim: 30 exec/s: 1398101 rss: 517Mb
#16777216 pulse cov: 7 ft: 7 corp: 6/151b lim: 30 exec/s: 1290555 rss: 519Mb
#18632135 NEW cov: 8 ft: 8 corp: 7/181b lim: 30 exec/s: 1330866 rss: 519Mb L: 30/30 MS: 2 ChangeBinInt-ChangeByte-
#18643672 NEW cov: 9 ft: 9 corp: 8/211b lim: 30 exec/s: 1331690 rss: 519Mb L: 30/30 MS: 2 CopyPart-ChangeBit-
#24568052 NEW cov: 10 ft: 10 corp: 9/241b lim: 30 exec/s: 1293055 rss: 523Mb L: 30/30 MS: 5 ChangeBit-ChangeByte-ChangeByte-ChangeBinInt-ChangeByte-
#24757679 NEW cov: 11 ft: 11 corp: 10/271b lim: 30 exec/s: 1303035 rss: 523Mb L: 30/30 MS: 2 ChangeBit-ChangeByte-
#26020842 NEW cov: 12 ft: 12 corp: 11/301b lim: 30 exec/s: 1301042 rss: 523Mb L: 30/30 MS: 3 CopyPart-ChangeByte-ChangeByte-
#26117144 NEW cov: 13 ft: 13 corp: 12/331b lim: 30 exec/s: 1305857 rss: 523Mb L: 30/30 MS: 2 ChangeByte-ChangeByte-
#26142896 NEW cov: 14 ft: 14 corp: 13/361b lim: 30 exec/s: 1307144 rss: 523Mb L: 30/30 MS: 2 ChangeBit-ChangeASCIIInt-
#26165012 NEW cov: 15 ft: 15 corp: 14/391b lim: 30 exec/s: 1308250 rss: 523Mb L: 30/30 MS: 1 ShuffleBytes-
#26773498 NEW cov: 16 ft: 16 corp: 15/421b lim: 30 exec/s: 1338674 rss: 525Mb L: 30/30 MS: 1 ChangeByte-
#29737219 NEW cov: 17 ft: 17 corp: 16/451b lim: 30 exec/s: 1292922 rss: 527Mb L: 30/30 MS: 1 ChangeByte-
#29758031 NEW cov: 18 ft: 18 corp: 17/481b lim: 30 exec/s: 1293827 rss: 527Mb L: 30/30 MS: 2 ChangeBit-ChangeBit-
#30749787 NEW cov: 19 ft: 19 corp: 18/511b lim: 30 exec/s: 1281241 rss: 527Mb L: 30/30 MS: 1 ChangeByte-
#30840239 NEW cov: 20 ft: 20 corp: 19/541b lim: 30 exec/s: 1285009 rss: 527Mb L: 30/30 MS: 2 CopyPart-ChangeByte-
#33075575 NEW cov: 21 ft: 21 corp: 20/571b lim: 30 exec/s: 1272137 rss: 527Mb L: 30/30 MS: 1 CMP- DE: "\x01\xc2"-
==1616955== ERROR: libFuzzer: deadly signal
#0 0x55afe81fb9ab in __sanitizer_print_stack_trace (/home/jiegec/inctf2021/find_plut0/fuzzer+0x11d9ab)
#1 0x55afe81495d1 in fuzzer::PrintStackTrace() (/home/jiegec/inctf2021/find_plut0/fuzzer+0x6b5d1)
#2 0x55afe8128de9 in fuzzer::Fuzzer::CrashCallback() (.part.0) (/home/jiegec/inctf2021/find_plut0/fuzzer+0x4ade9)
#3 0x55afe8128ea7 in fuzzer::Fuzzer::StaticCrashSignalCallback() (/home/jiegec/inctf2021/find_plut0/fuzzer+0x4aea7)
#4 0x7f35f823286f (/usr/lib/libpthread.so.0+0x1386f)
#5 0x55afe822cb6e in LLVMFuzzerTestOneInput (/home/jiegec/inctf2021/find_plut0/fuzzer+0x14eb6e)
#6 0x55afe8129adc in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/jiegec/inctf2021/find_plut0/fuzzer+0x4badc)
#7 0x55afe812b7b0 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/jiegec/inctf2021/find_plut0/fuzzer+0x4d7b0)
#8 0x55afe812c54c in fuzzer::Fuzzer::MutateAndTestOne() (/home/jiegec/inctf2021/find_plut0/fuzzer+0x4e54c)
#9 0x55afe812e027 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) (/home/jiegec/inctf2021/find_plut0/fuzzer+0x50027)
#10 0x55afe8114ddb in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/jiegec/inctf2021/find_plut0/fuzzer+0x36ddb)
#11 0x55afe8104293 in main (/home/jiegec/inctf2021/find_plut0/fuzzer+0x26293)
#12 0x7f35f804bb24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)
#13 0x55afe81042ed in _start (/home/jiegec/inctf2021/find_plut0/fuzzer+0x262ed)
NOTE: libFuzzer has rudimentary signal handlers.
Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal
MS: 1 ChangeBit-; base unit: 6dbb3c5ffc60d3db0d20a0d1010a5c26453b17f9
0x5e,0x5e,0x43,0x54,0x30,0xff,0x23,0x50,0x4d,0x33,0x26,0x9c,0xff,0x5e,0x2a,0xa1,0xa1,0xbb,0x5e,0x5e,0x52,0x5f,0x74,0x7a,0x5e,0x5e,0x4e,0x41,0xc2,0xa1,
^^CT0\xff#PM3&\x9c\xff^*\xa1\xa1\xbb^^R_tz^^NA\xc2\xa1
artifact_prefix='./'; Test unit written to ./crash-d4be3aa51be75ffc7155e25b8e79dc10df0e7b6d
Base64: Xl5DVDD/I1BNMyac/14qoaG7Xl5SX3R6Xl5OQcKh
可以看到结果已经输出了。最后再加个回车,发送到服务器就可以拿到 flag:
$ xxd ./crash-d4be3aa51be75ffc7155e25b8e79dc10df0e7b6d
00000000: 5e5e 4354 30ff 2350 4d33 269c ff5e 2aa1 ^^CT0.#PM3&..^*.
00000010: a1bb 5e5e 525f 747a 5e5e 4e41 c2a1 ..^^R_tz^^NA..
$ echo "" >> ./crash-d4be3aa51be75ffc7155e25b8e79dc10df0e7b6d
$ cat ./crash-d4be3aa51be75ffc7155e25b8e79dc10df0e7b6d | nc 34.94.181.140 4205
Hello there , i lost my dog pluto :(
Help me call him out please !!
................
|w00ff w00ff!! |
'''''V''''''''
.~````~.
.,/ \,.
( | (0 0) | )
( | ____ | )
(_/| \__/ |\_)
\__/\__/
'-..-'
inctf{PluT0_C0m3_&_g3t_y0uR_tr3aToz!}⏎
另外,还有其他正确的解:
$ xxd ./crash-cb2878d8e63e7546ad1ae8790e26732f7d92721
00000000: 4b71 7054 3027 5b28 65b3 9786 87d9 a58e KqpT0'[(e.......
00000010: d5ef b7fa 121f 743a ef13 b7bf ba41 0a ......t:.....A.
$ xxd ./crash-cda16159f443200b5c8b241ac810ca44851f3761
00000000: 4b71 7054 3027 5b28 65b3 9786 87d9 a58e KqpT0'[(e.......
00000010: d5ef b7fa 121f 743a ef13 b7bf ba21 ......t:.....!
P.S. 可能会出现一些不合法的答案:
# len=11
$ xxd ./crash-10346f4b2bb6482f0004f794033573f20fdab4a9
00000000: 5e5e 4354 30bd a1d2 cf3b 0100 3d33 ff00 ^^CT0....;..=3..
00000010: 001a 1f00 121f 74f3 00f8 8141 c241 ......t....A.A
# len=10
$ xxd ./crash-6e515694c0e594941a6518441538cfbcf590f7e3
00000000: 4a72 6f54 3083 1f6c a933 0000 03b4 8080 JroT0..l.3......
00000010: 5b75 0080 121f 743b fff7 80c0 be81 [u....t;......
这和 scanf %s 的行为有关:中间不能有空白字符(iswspace 函数,例如 0x09-0x0d 0x20)。