[pwnable.xyz] two targets 풀이
analysis
checksec으로 확인하면 PIE랑 RELRO가 걸려있지 않고 canary와 NX가 걸려있다.
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int cmd; // eax
char name[32]; // [rsp+10h] [rbp-40h] BYREF
_QWORD v5[4]; // [rsp+30h] [rbp-20h] BYREF
v5[3] = __readfsqword(0x28u); // cnry
setup();
memset(name, 0, 0x38uLL);
while ( 1 )
{
while ( 1 )
{
print_menu();
cmd = read_int32();
if ( cmd != 2 )
break;
printf("nationality: "); // cmd == 2
__isoc99_scanf("%24s", v5);
}
if ( cmd > 2 )
{
if ( cmd == 3 )
{
printf("age: ");
__isoc99_scanf("%d", v5[2]); // input age
}
else if ( cmd == 4 ) // get shell
{
if ( auth((__int64)name) ) // make auth 1
win();
}
else // cmd > 4
{
LABEL_14:
puts("Invalid");
}
}
else // cmd == 1
{
if ( cmd != 1 )
goto LABEL_14;
printf("name: ");
__isoc99_scanf("%32s", name);
}
}
}
익스할만한 부분이 보이지 않는다.
필요한 부분은 auth 함수에서 있을 것 같다.
name의 크기는 0x20이다.
_BOOL8 __fastcall auth(__int64 arg_name)
{
signed int i; // [rsp+18h] [rbp-38h]
char s1[8]; // [rsp+20h] [rbp-30h] BYREF
__int64 v4; // [rsp+28h] [rbp-28h]
__int64 v5; // [rsp+30h] [rbp-20h]
__int64 v6; // [rsp+38h] [rbp-18h]
unsigned __int64 cnry; // [rsp+48h] [rbp-8h]
cnry = __readfsqword(0x28u);
*(_QWORD *)s1 = 0LL;
v4 = 0LL;
v5 = 0LL;
v6 = 0LL;
for ( i = 0; (unsigned int)i <= 0x1F; ++i ) // loop 0x20
s1[i] = ((*(_BYTE *)(arg_name + i) >> 4) | (16 * *(_BYTE *)(arg_name + i))) ^ *((_BYTE *)main + i);// ((name byte/16) | (namebyte*16)) xor main byte
return strncmp(s1, &s2, 0x20uLL) == 0;
}
auth 함수이다.
여기서는 strncmp로 검사하여 반환한다.
s2와 s1이 같아야한다.
s2의 주소는 0x401d28이다.
그 값은
11 de cf 10 df 75 bb a5
43 1e 9d c2 e3 bf f5 d6
96 7f be b0 bf b7 96 1d
a8 bb 0a d9 bf c9 0d ff
이다.
그리고 main은
55 48 89 e5 48 83 ec 50
64 48 8b 04 25 28 00 00
00 48 89 45 f8 31 c0 e8
24 fe ff ff 48 8d 45 c0
으로 되어있다.
main을 먼저 보는 이유는
auth에서 name의 바이트를 가지고 or 연산 후 xor 연산을 하는데
시프트 연산 후 or 연산을 하는 것의 의미가 상위 4byte와 하위 4byte를 변경하는 것이다.
예를 들어 처음 문자가 0x41이라면 0x14로 바뀌고 main과 xor 연산을 하게 된다.
어떤 것과 main을 xor 한 것이 s2이어야 한다.
어떤 것은 따라서 main과 s2를 xor 한 것이다.
이렇게 계산해본다.
0xf5 57 f6 97 f5 46 96 44
0xd6 f5 97 c6 c6 16 56 27
0xf5 56 86 47 f5 37 37 96
0x3f 48 44 f7 26 f5 45 8c
이다.
따라서 우리가 name으로 넣을 문자열은 여기서 상위 4바이트와 하위 4바이트가 바뀐 문자열이다.
44 69 64 5f 79 6f 75 5f
72 65 61 6c 6c 79 5f 6d
69 73 73 5f 74 68 65 5f
c8 54 5f 62 7f 44 84 f3
바이트 순서대로 넣어봐야징
뭔가 되는 것 같다.
오 gdb로 안 뜯어보고도 바로 성공했다.
from pwn import *
p = remote('svc.pwnable.xyz', 30031)
#p = process('./challenge')
p.sendlineafter('> ', '1')
payload = p64(0x5f756f795f646944) + p64(0x6d5f796c6c616572) + p64(0x5f6568745f737369) + p64(0xf384447f625f54c8)
p.sendafter(': ', payload)
p.sendlineafter('> ', '4')
p.interactive()
다른 풀이도 있으니 참고하자