오랜만에 푸는 bof문제다..



문제를 들어가면 strcpy함수를 대놓고 쓰는 문제다.

여기서 취약점이 터진다.

하지만 이전에 풀던 lob와는 달리 ASCII Armor와 ASLR, 그리고 스택의 값은 실행 권한이 주어지지 않는 Non-Excutable stack이 설정되어있다.

먼저 ASCII Armor란 라이브러리의 함수 주소의 최상위 부분을 0x00으로 만들어 RTL공격 구성 시 연속적인 페이로드를 작성할 수 없게 만든 보안정책이다.

그리고 ASLR은 프로그램이 실행될 때 마다 랜덤으로 스택의 주소를 배정시키는 기법이다.

마지막으로 Non-Excutable stack은 NX라고도 부르며 스택영역에 실행권한을 주지 않아서 스택에 입력된 쉘코드가 실행되지 않게 하는 보안정책이다.


이 문제를 풀려면 이 모두를 우회해야 한다.

먼저 힌트를 보면 fake ebp라고 한다.

lob에서 fake ebp로 문제를 푼 적이 있는데 그때와 비슷하게 문제를 푸는 것은 아니였다.


일단  NX에 의해서 쉘코드를 스택에 입력하는 방법은 안먹힐 것이고, RTL 또한 ASLR과 아스키 아머에 의해 막힐 것이다.

힌트인 fake ebp를 잘 생각해보면 ebp 값을 속여서 문제를 풀어보라는 의미일 것이다.


찾아보니 주소가 랜덤하게 바뀌지 않는 got영역으로 fake ebp를 이용한 rtl기법을 사용할 수 있다.

그래서 일단 got의 주소를 찾았다.


$ readelf -a ./iron_golem | grep "got" 명령어로 찾는다.

got영역의 시작주소는 0x08049618이다.


그리고 실행함수를 찾았다.

execl함수를 이용했다.

execl함수의 주소는 0x7a5720이다. 

맨 앞 자리는 아스키 아머에 의해 0x00으로 채워져 있다.


이제 페이로드를 작성해야한다.

페이로드 작성에 앞서 


우선 이 문제의 스택을 살펴보자.

스택을 살펴보면 c소스에서는 256바이트의 버퍼를 할당해주었는데 디버거에서는 0x108바이트를 할당해주었다.

0x108바이트는 264바이트다. 즉 다시말해 8바이트의 더미 값이 더 추가된 것이다.

buf[256] | dummy[8] | sfp[4] | ret[4]

이렇게 구성되어 있다.

fake ebp를 하기 위해 일단 ret까지 오버플로우를 줘야하는데 buf에 264바이트의 쓰레기 값을 주고 sfp에 got영역의 값을 주어야 한다.

하지만 그냥 got영역의 값을 주면 안되고 got-8의 주소를 주어야 한다. 그 이유는 execl함수에 인자를 주어야 하는데 이 함수의 인자는 함수가 호출될 당시의 ebp 값을 기준으로 ebp+8, ebp+c ... 이런식으로 주게 되어있다.

아래에서 got의 값을 확인해보자.

got를 보면 0x0804954c가 있다. 이 주소가 가리키는 값을 보자.

1이다.


execl 함수의 사용법은 아래와 같다.

int execl( const char *path, const char *arg, ...);

먼저 첫번째 인자에 실행할 파일의 경로를 가리키는 값이 들어가고, 마지막 인자에는 0을 입력해야 한다.

근데 딱 들어맞게도 got-8을 ebp에 넣어주게 되면 execl함수의 인자로 (got-8) +8 인 got주소가 첫번째 인자로, 두번째 인자로 0이 들어간다.

추가적으로 이 execl() 함수가 실행되면 execl('1'); 의 형태로 실행이 된다.


sfp위치에는 got-8의 주소를 입력하고 ret에는 execl에 +3을 한 주소를 입력한다. execl에 +3을 하는 이유는 지금 ebp의 값을 조작해 놓은 상태인데 만약 execl함수의 주소를 그대로 쓰면 함수 프롤로그가 실행되면서 스택이 바뀐다고 한다. 그래서 +3을 하면 프롤로그가 실행되지 않아 스택의 값이 유지된다.

마지막으로 쉘을 실행시킬 소스를 작성한다.


컴파일 해주고 심볼릭 링크를 걸어주는데 이때 이름을 \x01로 변경해준다. 왜냐면 execl함수의 첫번째 인자로 실행되는 파일명이 \x01이기 때문이다.

이제 페이로드를 작성한다.


Payload

./iron_golem `python -c 'print "A"*264+"\x10\x96\x04\x08"+"\x23\x57\x7a\x00"'`


문제 클리어


-----------------------------------------------------------------------------------------------------------------------------------------


추가적으로 파이썬으로 익스를 짰다.


import struct
import os

p = lambda x : struct.pack('<I', x)

fake_ebp = 0x8049618
execl = 0x7a5720

payload = "A"*264
payload += p(fake_ebp - 0x8)
payload += p(execl + 3)

os.system('./iron_golem ' + payload[:-1])

'Wargame > FedoraCore BOF' 카테고리의 다른 글

Fedora Core3 evil_wizard -> dark_stone  (0) 2018.02.25
Fedora Core3 hell_file -> evil_wizard  (0) 2018.02.25
Fedora Core3 dark_eyes -> hell_fire  (0) 2018.02.24
Fedora Core3 iron_golem -> dark_eyes  (0) 2018.02.23
FedoraCore3 설명  (0) 2018.02.19

 [기본 룰]

1. single boot 금지

2. root exploit 금지

3. /bin/my-pass 명령에 LD_PRELOAD 사용 금지


[레벨업 패스워드 확인]

/bin/my-pass


[전용 게시판]

http://www.hackerschool.org/HS_Boards/zboard.php?id=bof_fellowship


[몹 리스트]

LEVEL1 (gate -> gremlin) :  simple bof

LEVEL2 (gremlin -> cobolt) : small buffer

LEVEL3 (cobolt -> goblin) : small buffer + stdin

LEVEL4 (goblin -> orc) : egghunter

LEVEL5 (orc -> wolfman) : egghunter + bufferhunter

LEVEL6 (wolfman -> darkelf) : check length of argv[1] + egghunter + bufferhunter

LEVEL7 (darkelf -> orge) : check argv[0]

LEVEL8 (orge -> troll) : check argc

LEVEL9 (troll -> vampire) : check 0xbfff

LEVEL10 (vampire -> skeleton) : argv hunter

LEVEL11 (skeleton -> golem) : stack destroyer

LEVEL12 (golem -> darkknight) : sfp 

LEVEL13 (darkknight -> bugbear) : RTL1

LEVEL14 (bugbear -> giant) : RTL2, only execve

LEVEL15 (giant -> assassin) : no stack, no RTL

LEVEL16 (assassin -> zombie_assassin) : fake ebp

LEVEL17 (zombie_assassin -> succubus) : function calls

LEVEL18 (succubus -> nightmare) : plt

LEVEL19 (nightmare -> xavis) : fgets + destroyers

LEVEL20 (xavis -> death_knight) : remote BOF 

from pwn import *
import re
import time

def main():
        r = remote("pwnable.kr", 9007)
        res = r.recvuntil("Ready") + r.recvuntil("\x0a")

        print res 
        time.sleep(3)
        for i in range(100):
                res = r.recvuntil("=") + r.recvuntil("\x0a")
                info = res.split(" ")
                p = re.compile("\d+")
                for i in range(0,2):
                        info[i] = p.findall(info[i])
                N = int("".join(info[0]))
                C = int("".join(info[1]))

                print "N : {0}, C : {1}\n".format(N, C)
                src = 0 
                des = N 
                chance_check = 0 

                while src <= des:
                        chance_check += 1
                        dat = ""
                        mid = (src + des) / 2 

                        for i in range(src, mid + 1): 
                                dat += str(i) + " " 
                        r.sendline(dat)
                        weight = r.recv()

                        if int(weight) == 9:
                                break
                        if int(weight) % 10 == 0:
                                src = mid + 1 
                        elif int(weight) % 10 == 9:
                                des = mid - 1 

                for i in range(C - chance_check + 1): 
                        if int(weight) % 10 == 0 and i == 0:
                                dat = str(int(dat)+1)
                        r.sendline(dat)
                        res = r.recv()
                        print res 
        res = r.recv()
        print res 

if __name__ == "__main__":
        main()



'Wargame > pwnable.kr' 카테고리의 다른 글

pwnable.kr cmd1  (0) 2018.06.15
pwnable.kr input  (0) 2018.06.14
[pwnable.kr] shellshock 1p  (0) 2016.08.20
[pwnable.kr] mistake 1p  (0) 2016.08.19
[pwnable.kr] uaf 8p  (0) 2016.08.15

아래로 스크롤

명령기술
Ctrl + E한 줄 아래로 스크롤하십시오.
Ctrl + D화면 절반을 스크롤 다운합니다 ( scroll 옵션을 사용하여 구성 가능).
Ctrl + F전체 화면을 아래로 스크롤하십시오.
z +창의 맨 위에있는 창 아래의 첫 번째 선을 그립니다.

커서 위치를 기준으로 스크롤

명령기술
창의 맨 위에있는 현재 행을 다시 그려서 커서를 행의 첫 번째 공백이 아닌 문자에 놓습니다.
ztz와 같지만 커서는 같은 열에 두십시오.
지.창의 가운데에있는 현재 행을 다시 그리기하고 행의 첫 번째 비 공백 문자에 커서를 놓습니다.
zzz와 같습니다. 동일한 열에 커서를 두십시오.
지-창의 맨 아래에있는 현재 행을 다시 그려서 커서를 행의 첫 번째 비 공백 문자에 놓습니다.
zbz-와 같지만 커서는 같은 열에 두십시오.

위쪽으로 스크롤

명령기술
Ctrl + Y한 줄 위로 스크롤하십시오.
Ctrl + U화면 반 스크롤 ( scroll 옵션을 사용하여 구성 가능).
Ctrl + B전체 화면을 위로 스크롤하십시오.
z ^창의 아래쪽에있는 창 위의 첫 번째 선을 그립니다.





Ref.https://code.i-harness.com/ko/q/4afcc




'OS > linux' 카테고리의 다른 글

/proc/self/cwd  (0) 2019.05.09
프로세스에서 사용중인 파일 디스크립터 찾기  (0) 2018.07.20
리눅스 넘버링?  (0) 2018.03.27
리눅스 세션 연결 시 history 자동 삭제하기  (0) 2018.03.20
[명령어] strings  (0) 2016.07.28

CTF에서 pwnable 문제를 풀 때 nc를 이용해서 문제에 접속하는 경우가 많다. pwn문제에 nc로 문제를 만들고 공개하는 방법에 대해서 알아보고 기록을 한다.

xinetd를 이용해 nc 포트를 열어서 nc로 문제를 공개하는 방법이 있다.


1.xinetd 패키지를 설치한다.

우분투 기준 $apt-get install xinetd를 입력하면 설치할 수 있다.


2. /etc/xinetd.d/서비스이름 으로 파일을 생성한다.

$vi /etc/xinetd.d/서비스이름


3. 그리고 해당 파일에 아래와 같이 설정을 해준다.

service 서비스이름

{

          disable            = no

          flags              = REUSE

          socket_type      = stream # 사용하는 소켓의 종료를 입력한다. stream은 tcp를 뜻함

          protocol          = tcp       

          wait            = no

          user              = SSo # 계정

          server          = /sbin/shutdown  # 서비스할 파일명

}


추가. .py를 실행하는 서버는

server          = /usr/bin/python
server_args     = /home/script.py
위처럼 작성해주면 된다.


4. 서비스설정을 해주었으면 포트를 개방해주는 설정을 하기위해 /etc/service 파일에 설정을 추가해준다.

해당파일의 맨 아래에 # Local services라는 스트링이 있는데 이 아래에 포트 내용을 추가해준다.

# Local services

서비스이름 1234/tcp


5. nc로 접근하면 서비스가 제대로 실행되는 모습을 확인할 수 있다.

$nc localhost 1234


Ref.http://lily.mmu.ac.kr/lecture/08sm/Fedora2/7jang/2.htm

'HACKING > System hacking' 카테고리의 다른 글

pwnable 풀 때 objdump  (0) 2018.02.25
GOT Overwrite  (0) 2018.02.24
GDB BP에 관한 것  (0) 2017.11.29
heapoverflow 문제 풀이3  (0) 2016.11.05
heap overflow 문제풀이2  (0) 2016.11.02

일반적으로 python에서 모듈을 import할때 일반적으로 시작부분에 선언 후 코드를 작성한다.

그런데 모듈을 처음에 import하는게 아니라 상황에 맞게 동적으로 모듈을 import할 수 있다.


방법은 importlib 패키지를 사용하거나 __import__()함수를 사용하면 된다.

사실 둘의 차이는 별로 없다.

일단 importlib은 import구문(statement)을 수행하는 하나의 package일 뿐이며, __import__()함수는 import구문을 구성하는 expression이기 때문이다.

즉 __import__()함수는 importlib보다 low level로 호출하는 것 뿐이다.


importlib

import하고 싶은 module명을 module_name이라는 변수로 받았다고 하자.

이런 경우 importlib을 이용해 아래처럼 간단히 이용할 수 있다.

import importlib

def load_module_func(module_name):
	mod = importlib.import_module(module_name)
	return mod

mod란 변수를 통해 module 안에 class를 호출하는 것이 가능하다.


__import__()

importlib 보다 좀 더 low level 로 __import__ 를 이용해서도 구현이 가능하다. 물론 이걸 사용하면, importlib 패키지 자체를 import 하는 과정은 생략해도 된다. __import__() 는 built-in 함수이기 때문에 그냥 사용하면 된다. 


def load_module_func(module_name):
	mod = __import__('%s' %(module_name), fromlist=[module_name])
	return mod 
Ref. http://bluese05.tistory.com/31


'Language > python' 카테고리의 다른 글

python dictionary에서 한글 입력  (0) 2018.06.05
[python] z3 설치  (0) 2018.02.10
[python] 단순 치환암호 복호화하기  (0) 2017.11.18
[python] try, finally  (0) 2017.11.18
python 코드 잘 짜는 방법  (0) 2017.08.21

코드게이트 문제를 풀다가 알게 된 라이브러리다.

수식을 주면 변수를 알아내도록 도와주는 도구임.


설치는 아래 링크를 참고

https://github.com/Z3Prover/z3

'Language > python' 카테고리의 다른 글

python dictionary에서 한글 입력  (0) 2018.06.05
[python] 동적 import  (0) 2018.02.11
[python] 단순 치환암호 복호화하기  (0) 2017.11.18
[python] try, finally  (0) 2017.11.18
python 코드 잘 짜는 방법  (0) 2017.08.21

https://websec.ca/kb/sql_injection


'HACKING > Web hacking' 카테고리의 다른 글

mysql group_concat  (0) 2018.04.04
SQL injection msql.innodb_table_stats  (0) 2018.02.24
LFI Exploit with PHP Protocols / Wrappers  (0) 2017.11.30
Knowing about Local File Inclusion(LFI)  (0) 2017.11.14
php extract 취약점  (3) 2017.08.09

RedVelvet

gdb를 이용해 리버싱을 해보면 우선 사용자의 input을 받은 다음 func1 ~ func15 까지 15개의 함수에 사용자 input을 2~3개씩 쪼개서 각 함수의 인자로 넣어준다. 그리고 함수안에서 어떠한 계산 후 계산의 결과와 input 값이 일치하는지 확인 후 일치하면 HAPPINESS!라는 문자열이 출력된다.

한 함수라도 통과하지 못하면 프로그램이 종료한다.

함수를 전부 통과하게되면 각 함수마다 한번씩 총 15번 HAPPINESS!라는 문자열이 출력된 후 전부 통과하게 되면 플래그를 뱉어준다.


처음에는 손으로 직접 리버싱하여 함수를 분석한 후 약간(?) 노가다로 키를 구하는 방식으로 하다가 더 편한 방법을 찾았다.

python에 z3라는 라이브러리를 이용하면 수학식을 계산해서 값을 뽑아줄 수 있다.

 

func10까지는 노가다로 구했다가 func11부터 15까지는 라이브러리를 이용해 풀었다.

아래는 키를 구하는 코드다.

#sha256_hash = 0a435f46288bb5a764d13fca6c901d3750cee73fd7689ce79ef6dc0ff8f380e5
from z3 import *

s = Solver()

for i in range(1,27):
        globals()['a%i'%i]=BitVec('a%i'%i,32)

#func1
s.add(a1 * 2 * (a1 ^ a2)  - a2 == 0x2a6a)
s.add(a1 > 0x55)
s.add(a1 <= 0x5f)
s.add(a2 > 0x60)
s.add(a2 <= 0x6f)

#func2
s.add(a2 % a3 == 0x7)
s.add(a2 > 0x5a)

#func3
s.add(a3 / a4 +(a4 ^ a3) == 0x15)
s.add(a3 <= 0x63)
s.add(a4 <= 0x77)

#func4
s.add(a5 == 0x5f)

#func5
s.add((a6 + a5)^(a5 ^ a6 ^ a5 ) == 0xe1)
s.add(a5 > 0x5a)
s.add(a6 <= 0x59)

#func6
s.add(a6 <= a7)
s.add(a7 <= a8)
s.add(a6 > 0x55)
s.add(a7 > 0x6e)
s.add(a8 > 0x73)
s.add((a7+a8) ^ (a6 + a7) == 0x2c)
s.add((a7 + a8)%a6 + a7  == 0xa1)

#func7
s.add(a8 >= a9)
s.add(a9 >= a10)
s.add(a8 <= 0x77)
s.add(a9 > 0x5a)
s.add(a10 <= 0x59)
s.add((a8 + a10) ^ (a9 + a10) == 0x7a)
s.add((a8 + a10) % a9 + a10 == 0x65)

#func8
s.add(a10 <= a11)
s.add(a11 <= a12)
s.add(a12 <= 0x72)
s.add((a10 + a11) / a12 * a11 == 0x61)
s.add((a12 ^ (a10 - a11)) * a11 == 0xffffd898)

#func9
s.add(a12 == a13)
s.add(a13 >= a14)
s.add(a14 <= 0x63)
s.add(a14 + a12 * (a14 - a13) - a12 == 0xfffffa5d)

#func10
s.add(a14 >= a15)
s.add(a15 >= a16)
s.add(a15 * (a14 + a16 + 1) - a16 == 0x3c9a)
s.add(a15 > 0x5a)
s.add(a15 <= 0x63)

#func11
s.add(a17 >= a16)
s.add(a16 >= a18)
s.add(a17 > 0x64)
s.add(a17 <= 0x68)
s.add(a16 + (a17 ^ (a17- a18)) - a18 == 0x46)
s.add((a17 + a18) / a16 + a16 == 0x44)

#func12
s.add(a18 >= a19)
s.add(a19 >= a20)
s.add(a19 <= 0x3b)
s.add(a20 <= 0x2c)
s.add(a18 + (a19 ^ (a20 + a19)) - a20 == 0x6f)
s.add((a19 ^ (a19 - a20)) + a19 == 0x65)

#func13
s.add(a22 + (a21 ^ (a20 + a22)) - a20 == 0x10d)
s.add((a22 ^ (a21 - a20)) + a21 == 0xb9)
s.add(a20 <= a21)
s.add(a21 <= a22)
s.add(a20 > 0x28)
s.add(a21 > 0x5a)
s.add(a22 <= 0x6d)

#func14
s.add(a22 + (a23 ^ (a22 + a23)) - a24 == 0xb9)
s.add(a22 >= a24)
s.add(a23 >= a24)
s.add(a23 <= 0x63)
s.add(a24 > 0x5a)

#func15
s.add((a26 ^ ((a25 - a24) * a25)) - a24 == 0x4be)
s.add((a24 ^ ((a26 - a25) * a26)) + a25 == 0xfffffbf6)
s.add(a25 >= a26)
s.add(a25 >= a24)
s.add(a26 > 0x5f)
s.add(a25 <= 0x6d)

print s.check()
#print s.model()

if (s.check() == sat):
        values =s.model()
        flag=""
        for i in range(1,27):
                obj = globals()['a%i' % i]
                char = values[obj].as_long()
                flag += chr(char)
        print flag


FLAG : What_You_Wanna_Be?:)_lc_la



lc_la라고 나오는데 이 값을 your flag에 입력하면 flag를 뱉지 않는다.

gdb로 어셈을 보다보면 sha256으로 내가 입력한 flag를 암호화한 다음 gdb 내에 있는 0a435f46288bb5a764d13fca6c901d3750cee73fd7689ce79ef6dc0ff8f380e5 이 값과 비교를 해서 같으면 플래그를 뱉어준다.

아마 저 암호화된 값은 진짜 flag를 SHA256으로 암호화 한 것으로 보인다.

게싱으로 lc를 la로 바꿔서 What_You_Wanna_Be?:)_la_la로 입력하면 플래그를 뱉어준다.


Ref. http://revers3r.tistory.com/103?category=112084

Ref. https://github.com/AnisBoss/CTFs/blob/master/Codegate%20CTF%202018/RedVelvet%20-%20254pts%20(Rev)/solve.py


'CTF > Codegate' 카테고리의 다른 글

codegate2019 20000  (0) 2019.02.07
codegate 2018 super marimo  (0) 2019.01.19
CodeGate2018 BaskinRobins31  (0) 2018.07.14

http://gnu-cse.tistory.com/30

'HACKING > Reversing' 카테고리의 다른 글

IDA 코드 패치 후 elf 저장하는 방법  (0) 2019.08.14
OllyDBG 단축키 모음  (0) 2018.01.14
어세블리어 REP, STOS, MOVS  (0) 2018.01.14

+ Recent posts