https://dreamhack.io/wargame/challenges/3/ basic_exploitation_001 문제 풀이

 

 

위와 같이 다운받은 파일에 실행 권한을 준 후 실행시키면 값을 입력받도록 하고 있고, 

아무런 값을 입력했더니 프로그램이 그냥 종료되는 것을 확인할 수 있었다.

 

소스코드의 내용을 살펴보자.

 

 

먼저, alarm_handler() 함수는 puts() 함수를 통해 TIME OUT을 출력하고 함수를 종료시키는데, 

이는 파일 실행 후 너무 오랜 시간 입력값을 받지 않으면 프로그램을 종료시키는 함수이다.

다음으로, 메인 함수에서 buf를 해당 크기만큼 할당하고, initialize() 함수를 실행시킨 후 gets() 함수로 입력받는다.

또한, read_flag() 함수를 통해 flag를 출력해주기 때문에 이 부분의 해당 주소값이 넣어진다면 문제가 해결될 것이다. 

 

gdb를 통해 파일을 실행시켜보자.

 

 

위와 같이 info function 명령어를 통해 read_flag의 주소값이 0x080485b9인 것을 확인할 수 있다.

이때 스택 버퍼의 크기는 0x80이었고, 스택은 ret/sfp/buf로 구성되어 있을 것이기 때문에

ret에 read_flag() 함수의 주소를 넣으면 해결될 것이다.

또한, sfp는 4바이트이므로 0x80+4=132바이트라는 것을 구할 수 있다.

 

이렇게 계산한 값을 pwntools를 이용하여 코드를 작성해보자.

 

 

접속 정보는 위와 같이 문제에서 확인할 수 있다.

 

 

아까 위에서 구한 값인 132만큼 a를 입력해준 후 read_flag() 함수의 주소에 해당하는 0x080485b9를

위와 같이 리틀 엔디안 방식으로 더해 payload를 전달해준다.

 

 

파일 저장 후 python 명령어로 실행시켜보면 위와 같이 플래그가 잘 출력되는 것을 확인할 수 있다.

'2021-1 STUDY > Pwnable Study' 카테고리의 다른 글

Week06_프로세스4  (0) 2021.08.28
Week05_BOF  (0) 2021.05.19
Week04_GDB 분석  (0) 2021.05.12
Week03_collision  (0) 2021.05.05
Week03_setuid  (0) 2021.05.05

https://pwnable.kr bof 문제 풀이

 

 

 

 

위와 같이 wget 명령어를 사용하면 해당 파일을 가상머신에서 바로 다운로드 받을 수 있다.

 

 

다운받은 파일에 실행 권한을 준 후 실행시켜 보고 아무런 내용을 입력하면 위와 같은 결과가 나왔다.

특정 크기가 넘어가면 오버플로우가 일어나 프로그램이 중지되고 종료되는 것을 확인할 수 있었다.

 

다음으로 코드의 내용을 살펴보자.

 

 

메인 함수를 보면 0xdeadbeef를 인자로 갖는 func() 함수를 호출하고 있고

func() 함수를 보면 크기가 32인 overflowme를 선언한 후 gets() 함수를 통해 키값을 확인해 주고 있다.

여기서 인자로 받아온 key와 0xcafebabe를 비교한 값이 같게 만들어 주어야 한다.

이때 크기를 따로 검사하지 않아 overflowme에 할당된 크기를 초과하여 입력할 수 있는 gets() 함수의

취약점을 이용하여 공격을 진행할 것이다.

 

이제 함수를 확인하기 위해 gdb를 이용해보자.

 

 

disas func 명령어를 통해 코드를 확인해보면 CALL 명령어가 사용된 함수 호출 부분을 확인할 수 있다.

lea 명령어를 통해 ebp-0x2c 부분이 버퍼(overflowme)의 시작 주소라는 것을 알 수 있다.

또한, cmp 명령어(if문)의 주소가 40만큼 떨어져 있는 것을 확인할 수 있다.

 

 

따라서 위와 같이 func에 40을 더해준 주소값에 breakpoint를 걸어준 후 실행을 한다.

 

 

위와 같이 해당 cmp 부분에 bp가 잘 걸린 것을 확인할 수 있다.

 

 

또한 esp 값을 확인해 보면 위와 같고, aaaaa를 입력했었기 때문에 61이라는 수가 5번 들어가 있는 것을 확인할 수 있다.

이 때 deadbeef의 주소는 0xffffd010이고, 버퍼의 시작 주소는 0xffffcfdc이다.  

 

 

위의 두 값을 뺀 값을 통해 주소 사이의 거리를 알 수 있고, 이때 p 명령어를 사용한다.

결과값은 0x34로 52만큼 떨어져 있다는 것을 확인할 수 있다.

 

다음으로는 pwntools를 사용하여 공격을 하는 코드를 작성해보자.

 

 

아까 문제 코드에서 key 값을 0xcafebabe와 같게 해주어야 했기 때문에

위에서 구한 거리값인 52에 리틀 엔디안 방식으로 더해 payload를 전달해준다.

 

 

파일 저장 후 python 명령어로 실행시켜 보면 위와 같이 잘 실행되는 것을 확인할 수 있다.

 

 

ls를 해보았더니 flag 파일이 있었고, cat 명령어를 통해 위와 같이 flag를 알아낼 수 있었다.

 

'2021-1 STUDY > Pwnable Study' 카테고리의 다른 글

Week06_프로세스4  (0) 2021.08.28
Week05_basic_exploitation_001  (0) 2021.05.19
Week04_GDB 분석  (0) 2021.05.12
Week03_collision  (0) 2021.05.05
Week03_setuid  (0) 2021.05.05

1. GDB란?


1) 개념


- GNU Debugger 
- 오픈 소스로 공개되어 있는 무료 디버거
- GNU 소프트웨어 시스템을 위한 기본 디버거
- 다양한 유닉스 기반의 시스템에서 동작하는 디버거
- C, C++, 포트란 등의 여러 프로그래밍 언어를 지원

2) 기능


- 컴퓨터 프로그램의 실행을 추적하고 수정할 수 있는 많은 기능들을 제공
- 사용자는 프로그램의 내부 변수들의 값을 주시하거나 변경 가능
- 프로그램의 일반적인 실행 과정과 독립적으로 함수를 호출 가능
 
3) 명령어


① 시작과 종료
- gdb [프로그램명] : 시작
- q(quit) or ctrl+d : 종료


② 소스보기(list or l)
- list : 메인함수 기점으로 소스 출력
- list 10 : 10행을 기준으로 출력
- list func : func 함수의 소스 출력
- list- : 출력된 행의 이전행 출력
- list file.c : file의 func 함수 부분 출력
- file.c:10 : file의 10행을 기준으로 출력 


③ 브레이크 포인트(break or b)
- b func : func 함수의 시작부분에 브레이크 포인트 설정
- b 10 : 10행에 브레이크 포인트 설정
- b *0x8049000 : 특정 주소에 브레이크 포인트 설정
- tb : b와 같으나 1회용 브레이크 포인트. 문법은 b와 동일
-info b : 현재 브레이크 포인트 보기
- cl : 브레이크 포인트 지우기
- d : 모든 브레이크 포인트 지우기


④ 진행 명령어
- run(r) : 프로그램 수행
- kill(k) : 프로그램 수행 종료
- step(s) : 현재 행 수행 후 정지, 함수 호출 시 함수 안으로 들어감
- next(n) : 현재 행 수행 후 정지, 함수 호출시 함수 수행 다음 행으로 감
- n 5 : n 다섯번 수행
- continue(c) : 다음 브레이크 포인트까지 진행
- u : 현재 루프를 빠져나감
- finish : 현재 함수를 수행하고 빠져나감
- return : 현재 함수를 수행하지 않고 빠져나감
- return 123 : 위와 같으나 리턴값 지정
- si : 어셈블리 명령어 단위의 수행 (step)
- ni : 어셈블리 명렁어 단위의 수행 (next)


⑤ 와치 포인트
- watch 변수명 : 특정변수에 와치 포인트를 설정하고, 특정변수가 바뀔 때마다 브레이크가 걸리면서 이전/현재 값을 출력


⑥ 변수출력
- info locals : 현재 스택의 로컬변수 모두를 출력
- info variables : 전역변수 모두 출력 (스압)
- p [변수명] : 해당변수 value 출력
- 포인터변수 입력시 주소값 출력, *포인터변수명 -> 실제 value 출력
- p $[레지스터명] : 레지스터값 출력
- p *[포인터] : struct/class의 배열일 때 배열의 크기를 알림
- p /[출력형식][변수명] : 출력형식에 맞추어 변수값 출력


⑦ 스택 상태 검사
- info f : 스택 프레임 내용 출력
- info args : 함수 호출시 인자를 출력
- info locals : 함수의 지역변수를 출력
- info catch : 함수의 예외 핸들러를 출력
- bt : 전체 스택 프레임 출력(콜스택)
- frame [스택번호] : 스택번호의 스택 프레임으로 이동


⑧ 기타
- disas [함수명] : 특정함수의 어셈블리 코드 출력
- disas [주소] [주소] : 주소 사이의 어셈블리 코드 출력
- call [함수명(인자)] : 특정 함수를 인자값으로 호출
- jump *[주소] : 주소로 강제적으로 분기
- jump [행번호] : 특정 행으로 강제 분기
- jump [함수명] : 특정 함수로 강제 분기
- info signals : signal 종류 출력

 

2. GDB 코드 분석

 

 

먼저 분석할 코드를 보면 위와 같다. 

3개의 매개변수를 갖는 function() 함수가 있고 char은 크기가 1byte이기 때문에

buffer1은 15byte, buffer2는 10byte를 갖는다.

 

 

위와 같이 작성한 코드를 32bit로 컴파일한다.

 

 

위와 같이 gdb [파일이름] 이라는 명령어를 입력하여 gdb에 들어갈 수 있다.

 

 

pdisas 명령어는 disas(함수의 어셈블리 코드 출력)를 색깔을 입혀 출력해주는 gdb-peda의 명령어이다.

main() 함수의 어셈블리 코드를 확인해보면 위와 같다.

위와 같은 코드는 메모리 구조의 code segment 부분에 해당한다.

 

 

또한, main() 함수 안의 function() 함수의 어셈블리 코드도 확인할 수 있고, 

이도 마찬가지로 메모리 구조의 code segment 부분에 해당한다.

이때 여기서 전역변수 선언을 하지 않았기 때문에 데이터 영역에는 아무것도 없고,

스택 영역에는 환경변수, argc. argv pointer 등이 들어간다. (이 코드에서는 환경변수)

 

 

메인 함수를 실행했을 때 eip는 맨 첫줄일 것이므로 

위와 같이 b *[주소] 명령어를 입력하여 맨 처음에 브레이크 포인트를 걸어준다.

 

 

run을 해보면 bp를 건 주소에서 잘 걸리는 것을 확인할 수 있다.

 

 

eip 명령어는 앞으로 수행할 명령어의 주소 위치를 담고 있다.

info reg eip를 해보면 메인 함수의 맨 처음 주소를 가리키고 있는 것을 확인할 수 있다.

 

 

메인 함수의 프롤로그를 살펴보자.

push ebp : ecx-0x4를 해준 값을 ebp에 넣음

mov ebp, esp : esp값을 ebp에 복사 → 두 값이 가르키는 주소가 같아짐

sub esp,0x4 : esp-0x4 → 공간 확장의 의미

push 0x3 : 스택에 인자값을 넣음 → 스택 구조는 반대이기 때문에 1, 2, 3의 순서대로 실행

call 0x804843b <function> : function 함수로 인자값 전달하며 이동

 

 

 

call 명령어를 실행한 후 돌아와야 할 주소는 eip 레지스터에 저장된다.

이때 돌아와야 할 주소의 바로 위 주소(0x8048478)에 bp를 걸어준다.

 

 

위는 run을 했을 때 결과 화면이다.

 

 

위와 같이 continue 명령어를 통해 다음 bp로 넘어갈 수 있다.

 

 

si 명령어를 통해 다음 줄을 실행할 수 있다. 

 

 

 

info reg eip를 통해 주소를 확인해 보면 function 함수의 주소가 들어가 있는 것을 확인할 수 있다.

 

 

function 함수의 프롤로그를 살펴보자.

push ebp : 메인함수에서 사용하는 ebp를 다시 저장

mov ebp, esp : ebp에 esp 값을 이동 → base pointer 값

sub esp,0x28 : 28byte 만큼의 공간 확장

 

 

아까 buffer1은 15, buffer2는 10이었는데 이 버전에서는 word(4byte) 단위로 할당되기 때문에

buffer1은 16byte, buffer2는 12byte가 할당되므로 위와 같이 28byte만큼의 공간을 확장시킨다.

 

 

함수의 에필로그를 확인해보자.

leave 명령어는 mov %ebp/%esp, pop %ebp를 의미하고 

ret 명령어로 return해준다.

 

 

add esp,0x10 : 어셈블리는 16진수이므로 16만큼의 크기를 더한다는 의미

                       → 스택에서 더한다 : 반환한다는 의미

'2021-1 STUDY > Pwnable Study' 카테고리의 다른 글

Week05_basic_exploitation_001  (0) 2021.05.19
Week05_BOF  (0) 2021.05.19
Week03_collision  (0) 2021.05.05
Week03_setuid  (0) 2021.05.05
Week02_Pwntools  (0) 2021.04.28

 

먼저 ssh를 이용하여 위와 같이 pwnable.kr에 접속한다.

 

 

ls를 해주면 세가지 파일을 볼 수 있고 하나씩 실행시켜보면 각각 위와 같은 결과들을 볼 수 있다.

 

 

각각의 파일의 속성들을 봤더니 flag 파일의 권한을 얻을 수 있도록 변경해주는건가?라고 생각했다.

 

 

프로그램을 분석해보기 위해 우선 col.c의 코드를 살펴보았다.

메인함수를 보면 argc와 argv를 매개변수로 입력받고 argc와 argv 문자열의 길이에 따라 각각의 결과를 출력한다.

또한 hashcode와 check_password를 비교하여 값이 같다면 flag 파일을 보여주는 것임을 알 수 있었다.

다음으로 check_password 함수를 보면 반복문을 통해 5번 반복하는동안 res라는 변수에 ip[i]값을 더해준다는 것을

알 수 있었고 hashcode에는 0x21DD09EC라는 값이 들어 있으니 이 두 값을 같게 만들어 주어야 플래그를 얻어낼 수 있다.

여기서 int형 포인터로 형변환을 해주었기 때문에 4byte씩 읽어들이는데 passcode 길이는 20byte이므로 총 5번 접근한다.

 

 

계산기를 이용해 21DD09EC를 5로 나눠주면 몫은 06C5CEC8, 나머지는 4이다.

이때 5번 접근하기 때문에 06C5CEC8를 4번 더하고 06C5CEC8+4=06C5CECC를 1번 더하면 hashcode 값과 같아진다.

여기서 주의할 점은 스크립트를 little-endian 방식을 사용하고 있기 때문에 이에 맞춰 작성해야 한다.

 

 

따라서 이와 같이 값을 넣어주면 플래그를 확인할 수 있다.

'2021-1 STUDY > Pwnable Study' 카테고리의 다른 글

Week05_BOF  (0) 2021.05.19
Week04_GDB 분석  (0) 2021.05.12
Week03_setuid  (0) 2021.05.05
Week02_Pwntools  (0) 2021.04.28
Week01_메모리구조  (0) 2021.03.31

▶ SetUID?

 

- 실행권한에 관련된 개념

- SetUID가 설정된 파일을 실행할 때 일시적으로 파일 소유자의 권한을 얻게 됨

- ex) root 권한으로 지정된 프로그램에 SetUID가 지정되어 있다면 실행할 때 root 권한으로 실행

- SetUID를 적용하기 위해서는 기존의 허가권 앞에 4를 붙여야 함

- 기존 권한에 실행권한이 없으면 S, 있으면 s로 표시됨

- 어떤 파일을 SetUID로 만드는 방법

chown root

chmod 4755

- /usr/bin/passwd 파일

: SetUID가 적용된 가장 대표적인 파일

: 계정의 비밀번호를 변경할 수 있도록 하는 명령어 실행 파일

: /etc/passwd의 권한은 root만 변경 가능

: /usr/bin/passwdSetUID가 적용되어 있지 않다면 일반 사용자들은 항상 관리자를 거쳐 비밀번호를 변경해야 함

일반 사용자도 root의 권한으로 /etc/passwd 파일을 수정 가능하도록 설정

 

 

 

▶ id 명령 SetUID 프로그램화

 

 

위에서 볼 수 있듯이 id 명령어의 소유자는 ubuntu이다.

 

 

which를 이용하여 id의 위치를 찾는다.

 

 

id의 위치를 myid라는 이름으로 복사한 후 ls를 통해 확인할 수 있다.

 

 

myid를 실행시켜보면 위에서 id 명령어를 이용한 것과 동일한 결과가 나온다.

 

 

파일을 SetUID로 만들기 위해 chown과 chmod를 이용한다.

먼저 chown root로 소유자를 root로 변경한다.

 

 

그다음에 chmod 4755로 특수 권한을 부여한다. ( 4 : setUID, 755 : 권한 )

위에서 myid가 setuid로 변경이 된 것을 확인할 수 있다.

 

 

마지막으로 myid를 실행해보면 euid=0(root)을 통해 루트 권한으로 실행된 것을 확인할 수 있다.

 

 

 

▶ cat 명령 SetUID 프로그램화

 

 

which를 이용하여 cat 명령의 위치를 찾는다.

 

 

해당 경로의 명령 파일을 mycat으로 복사한다. ls를 통해 확인할 수 있다.

 

 

chown으로 root 권한 갖기

 

 

chmod로 실행 권한 획득한 후 mycat을 실행시켜보면 root 권한으로 실행된 것을 확인할 수 있다.

'2021-1 STUDY > Pwnable Study' 카테고리의 다른 글

Week05_BOF  (0) 2021.05.19
Week04_GDB 분석  (0) 2021.05.12
Week03_collision  (0) 2021.05.05
Week02_Pwntools  (0) 2021.04.28
Week01_메모리구조  (0) 2021.03.31

1. Pwntools

1) CTF 프레임워크이자 익스플로잇을 쉽게 짤 수 있게 해주는 라이브러리

2) 설치방법

- apt-get update 
- apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential 
- python3 -m pip install --upgrade pip
- python3 -m pip install --upgrade pwntools

 

2. Pwntools 사용법

1) pwn 모듈 import

→ from pwn import *

2) 파일 연결

- 원격인 경우 : remote(IP, PORT)

- 로컬인 경우 : process(PATH)

    → 해당 프로그램을 실행시킨 후 입력을 대기한다.

    → process()로 실행시킨 프로그램은 입출력을 화면에 직접 할 수 없고 변수를 통해 함수를 호출하는 방법을 이용해야 한다.

- ssh인 경우 : ssh(USERNAME,IP,PORT,PASSWORD)

3) 데이터 받아오기 

- 한 줄 받아오기 : recvline()

- str까지 받아오기 : recvuntil(str)

- int만큼 받아오기 : recv(int)

4) 데이터 보내기

- value 값 보내기 : send(value)

- 데이터 한 줄 보내기 : sendline(value)

- 쉘에 직접 명령을 전송, 수신하기 : interactive()

    → 프로그램에 직접 입출력 조작이 필요할 때 이 함수를 호출

 

3. Pwntools 자주 쓰이는 모듈, 함수

1) pwnlib.context

- pwntools 설정에 편리

- context.log_level='debug' : exploit에 어려움이 있을 때 세팅

2) remote, listen, ssh, process

- pwnlib.tubes

- CTF에서 가장 일반적으로 쓰이고 편리한 기능

3) log

- pwnlib.log

4) shellcraft

- pwnlib.shellcraft

- shellcode 라이브러리

5) gdb.debug와 gdb.attach

- pwnlib.gdb

- 자동으로 breakpoint 설정하고 exploit에 반복을 만듦

 

4. 연습문제

 

 

먼저 chall.c 코드를 통해 소스코드를 확인할 수 있다.

이 코드는 랜덤으로 결정되는 a와 b를 합한 값을 입력받아 답이 맞다면 correct, 다르다면 wrong을 출력한다.

 

 

따라서 문제를 해결하기 위해 파이썬으로 위와 같은 코드를 작성했다.

먼저 swing_pwn_chall이라는 파일은 로컬 파일이기 때문에 process()함수를 이용하여 연결해주었고

위 코드에서 20번 반복되었기 때문에 반복문을 사용해준 후 

recvuntil()을 사용하여 해당 문자의 앞까지 데이터를 받아올 수 있도록 하고

sendline()을 사용하여 a와 b에 저장된 값을 int형으로 변환해준 데이터를 보낼 수 있도록 한다.

이 값을 recvline()을 사용하여 한 줄을 보내주고 이와 같이 입출력 조작을 해주었으므로 interactive() 함수를 이용한다.

 

 

위와 같이 작성한 코드를 실행시키면 correct와 flag가 잘 출력되는 것을 확인할 수 있다.

'2021-1 STUDY > Pwnable Study' 카테고리의 다른 글

Week05_BOF  (0) 2021.05.19
Week04_GDB 분석  (0) 2021.05.12
Week03_collision  (0) 2021.05.05
Week03_setuid  (0) 2021.05.05
Week01_메모리구조  (0) 2021.03.31

 

 

 

1. TEXT (CODE) 영역

 

- 유저가 작성한 소스코드가 들어가는 영역

 

- 실행할 프로그램의 코드가 저장되는 영역으로 바이너리화되어 저장

 

- 실행 파일을 구성하는 명령어들이 올라가는 메모리 영역으로 함수, 제어문, 상수 등이 지정

 

 

 

2. DATA 영역

 

- 전역변수와 static 변수가 할당되는 영역

 

- 프로그램의 시작과 동시에 할당되며 프로그램이 종료되어야 메모리가 소멸

 

 

 

3. BSS 영역

 

- 프로그램에서 사용될 변수들이 실제로 위치하는 공간

 

- 변수 선언 순서에 따라 주소값이 커지는 구조

 

 

 

4. HEAP 영역

 

- 동적으로 할당되는 변수의 데이터들이 위치하는 공간

 

- malloc 등의 함수 이용

 

- 프로그래머가 할당 및 해제하는 메모리 공간

 

- 장점 : 프로그램에 필요한 개체의 개수나 크기를 미리 알 수 없는 경우 사용 가능

 

- 단점 : 할당 작업이나 해제 작업으로 인한 속도 저하

 

 

 

5. STACK 영역

 

- 함수에 대한 정보를 포함

 

- 함수 안에 포함되어 있는 지역 변수의 내용들을 포함

 

- 프로그램이 자동으로 사용하는 임시 메모리 영역

 

- 함수 호출 완료 시 사라짐

 

- 장점 : 낭비되는 공간이 없고 하나의 명령만으로 조작 가능

 

- 단점 : 한계를 초과하도록 삽입할 수 없음 ( 유연성 부족 )

 

 

※ HEAP과 STACK 영역은 사실 같은 공간을 공유하는데, HEAP은 메모리 위쪽, STACK은 메모리 아래쪽부터 할당되는 방식

 

→ 각 영역이 상대 공간을 침범하는 일이 발생할 수 있고, 이를 각각 HEAP OVERFLOW, STACK OVERFLOW라고 한다.

 

 

 

 

'2021-1 STUDY > Pwnable Study' 카테고리의 다른 글

Week05_BOF  (0) 2021.05.19
Week04_GDB 분석  (0) 2021.05.12
Week03_collision  (0) 2021.05.05
Week03_setuid  (0) 2021.05.05
Week02_Pwntools  (0) 2021.04.28

+ Recent posts