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

▶ SQLI(SQL Injection)

 

- 악의적인 사용자가 보안상의 취약점을 이용하여, 임의의 SQL 문을 주입하고 실행되게 하여

  데이터베이스가 비정상적인 동작을 하도록 조작하는 행위

 

▶ 코드 분석 

 

from core.libs import insert_to_params_urls // URL 입력 함수

 

from wordlists import sqli_payloads,sql_err // 페이로드 전달 함수

페이로드 전달해주고 오류 확인해주는 함수로 보임

 

from urllib.parse import urlparse // URL 구문 분석 함수

URL 6개의 구성 요소로 구문 분석하여 6개 항목 네임드 튜플 반환

 

from re import findall // 문자열 패턴 찾아주는 함수

 

from queue import Queue // queue 사용하기 위해 import

 

from threading import Thread // thread 다루기 위해 사용

 

 

 

def start(op,http):  op와 http 받아와서 시작

    for method in op['methods']:

        for payload in sqli_payloads: 전송된 데이터들 한 줄씩 받아오기

            n = insert_to_params_urls(op['url'],payload) 해당 url과 payload 함수로 전송

※ def insert_to_params_urls(url,text,single=True,debug=False):

        u = list()

        try:

            if len(url.split('?')) >= 1: url에서 '?'라는 문자로 나눈 개수가 1보다 크거나 같으면

                for param in url.split('?')[1].split('&'): 위에서 나눈 값에서 '&'라는 문자로 문자열 나누기

                     u.append(url.replace(param,param + text))

                      param 값을 param에 text를 더해준 값으로 바꿔준 후 list에 요소 추가

            return remove_dups(u) 리스트 반환

        except Exception as e: 예외처리 오류가 발생하면 해당 블록 수행

            if debug:

                print(f'[insert_to_params_urls] {e}')

            return list()

 

            for url in n:

                if method == 'GET': 만약 method가 GET 방식이라면 해당 method 값 반환

                                          send 함수로 연결되고 method방식과 url 전송

                    r = http.send(method,url)

                else: 아니면 url에서 '?' 문자로 나눠주고 url쿼리의 URL 항목을 튜플 형태로 분석하여 반환

                    r = http.send(method,op['url'].split('?')[0],body=urlparse(url).query)

                for s,v in sql_err.items(): item() 함수 이용하여 method, url, params, payload, match 한번에 반환

                    hmm = findall(v,r.content.decode('utf-8')) findall() 함수 이용(정규표현식에 해당)

                                                                          문자열 중 패턴과 일치되는 모든 부분을 찾아내는 함수

                    for i in hmm: 해당하는 값을 찾았다면 요소 반환

                        if i:

                           return {

                                    'method':method,

                                    'url':url.split('?')[0],

                                    'params':urlparse(url).query,

                                    'payload':payload,

                                    'match':v

                                    }

            return {}

 

 

 

def main(opts,r):

    c = start(opts,r) start 함수 호출

    if c:

        print(f'''[SQLI] {c["url"]} 반환값 있다면 url 출력

\tParams: {c["params"]}

\tMethod: {c["method"]}

\tPayload: {c["payload"]}

\tMatch: {c["match"]}

''')

 

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

scant3r_SSTI  (0) 2021.05.05
Web Scanner  (0) 2021.04.11
Python  (0) 2021.03.27

+ Recent posts