Skip to content

Commit 11e11fc

Browse files
committed
add HITCON CTF 2021
1 parent ece9c25 commit 11e11fc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1568
-0
lines changed

README.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ And you can find me via:
2424

2525
## **Table of Content**
2626

27+
* [HITCON 2021](#W3rmup-PHP)
28+
* [W3rmup PHP](#W3rmup-PHP)
29+
* [One-Bit Man](#One-Bit-Man)
30+
* [Metamon Verse](#Metamon-Verse)
31+
* [FBI Warning](#FBI-Warning)
32+
* [Vulpixelize](#Vulpixelize)
33+
2734
* [HITCON 2020](#oShell)
2835
* [oShell](#oShell)
2936
* [oStyle](#oStyle)
@@ -77,6 +84,149 @@ And you can find me via:
7784

7885
<br>
7986

87+
## **W3rmup PHP**
88+
89+
Difficulty: **★★**
90+
Solved: **22 / 666**
91+
Tag: **PHP**, **Code Review**, **YAML** ,**Command Injection**
92+
93+
#### Source Code
94+
95+
* [Source](hitcon-ctf-2021/W3rmup-PHP/)
96+
97+
#### Idea
98+
99+
* [The Norway Problem](https://hitchdev.com/strictyaml/why/implicit-typing-removed/), the country code of Norway (NO) becomes `False` in YAML
100+
* Bypass the `escapeshellarg` by the logic problem of `count()` + `unset()`
101+
102+
#### Solution
103+
104+
* TBD
105+
106+
#### Write Ups
107+
108+
* TBD
109+
110+
111+
## **One-Bit Man**
112+
113+
Difficulty: ****
114+
Solved: **49 / 666**
115+
Tag: **PHP**, **Code Review**
116+
117+
#### Source Code
118+
119+
* [Source](hitcon-ctf-2021/One-Bit-Man/)
120+
121+
#### Idea
122+
123+
You can flip 1-bit on any file of the latest version of WordPress and you have to pwn the server.
124+
125+
#### Solution
126+
127+
Flip the position `5389` of the file `/var/www/html/wp-includes/user.php` to NOP the NOT (`!`) operation.
128+
129+
```php
130+
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
131+
return new WP_Error(
132+
```
133+
134+
#### Write Ups
135+
136+
* TBD
137+
138+
139+
140+
## **Metamon Verse**
141+
142+
Difficulty: **★★★☆**
143+
Solved: **9 / 666**
144+
Tag: **NFS**, **SSRF** ,**RCE**
145+
146+
#### Source Code
147+
148+
* [Source](hitcon-ctf-2021/oShell/)
149+
150+
#### Idea
151+
152+
The idea is using the SSRF to communicate with the local NFS/RPC server to get the RCE. To complete the exploit, you have to:
153+
154+
1. Construct the `RPC/PORTMAP_CALL` packet and send to `gopher://127.0.0.1:111/` to get the port of `mountd` service.
155+
2. Construct the `RPC/MNT_CALL` packet and send to `gopher://127.0.0.1:<mnt-port>/` to get the file-handler of `/data` volume (remember to specify `CURLOPT_LOCALPORT` to bypass the authentication)
156+
3. Construct the `RPC/NFS_CALL` packet and send to `gopher://127.0.0.1:2049/` to create a SYMLINK (remember to specify `CURLOPT_LOCALPORT` to bypass the authentication)
157+
4. Symlink the `/app/templates/index.html` to a controllable file to get a SSTI and get the RCE!
158+
159+
#### Solution
160+
161+
An dirty exploit code can be found [here](https://gist.github.com/orangetw/6d34ff98a6332bc0523b35ea952a790d)
162+
163+
#### Write Ups
164+
165+
* TBD
166+
167+
168+
## **FBI Warning**
169+
170+
Difficulty: ****
171+
Solved: **25 / 666**
172+
Tag: **MISC**, **OSINT** ,**PHP**, **Code Review**
173+
174+
#### Source Code
175+
176+
* [Source](hitcon-ctf-2021/FBI-Warning/)
177+
178+
#### Idea
179+
180+
The website uses a famous Message Board project [futaba-ng](https://github.com/futoase/futaba-ng), and the ID generation is based on `REMOTE_ADDR`:
181+
182+
```php
183+
define("IDSEED", 'idの種'); //idの種
184+
...
185+
$now.=" ID:".substr(crypt(md5($_SERVER["REMOTE_ADDR"].IDSEED.gmdate("Ymd", $time+9*60*60)),'id'),-8);
186+
```
187+
188+
#### Solution
189+
190+
Because of the known IP prefix, you can identify the IP address of Ωrange by brute-force easily.
191+
192+
```php
193+
var_dump( substr(crypt(md5("219.91.64.47"."idの種"."20211203"),"id"),-8) == "ueyUrcwA" )
194+
// bool(true)
195+
```
196+
197+
#### Write Ups
198+
199+
* TBD
200+
201+
202+
203+
## **Vulpixelize**
204+
205+
Difficulty: **★☆**
206+
Solved: **41 / 666**
207+
Tag: **Browser**, **Feature**
208+
209+
#### Source Code
210+
211+
* [Source](hitcon-ctf-2021/Vulpixelize/)
212+
213+
#### Idea
214+
215+
Use the Chrome new feature [Text Fragments](https://wicg.github.io/scroll-to-text-fragment/) to extract the flag.
216+
217+
218+
#### Solution
219+
220+
* TBD
221+
222+
#### Write Ups
223+
224+
* TBD
225+
226+
227+
228+
229+
80230
## **oShell**
81231

82232
Difficulty: **★★**
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
2+
<html><head>
3+
<meta charset="UTF-8"/>
4+
<!-- meta HTTP-EQUIV="pragma" CONTENT="no-cache" -->
5+
<STYLE TYPE="text/css">
6+
<!--
7+
body,tr,td,th { font-size:12pt }
8+
a:hover { color:#DD0000; }
9+
span { font-size:20pt }
10+
small { font-size:10pt }
11+
-->
12+
</STYLE>
13+
<title>画像掲示板</title>
14+
<script language="JavaScript"><!--
15+
function l(e){var P=getCookie("pwdc"),N=getCookie("namec"),i;with(document){for(i=0;i<forms.length;i++){if(forms[i].pwd)with(forms[i]){pwd.value=P;}if(forms[i].name)with(forms[i]){name.value=N;}}}};onload=l;function getCookie(key, tmp1, tmp2, xx1, xx2, xx3) {tmp1 = " " + document.cookie + ";";xx1 = xx2 = 0;len = tmp1.length; while (xx1 < len) {xx2 = tmp1.indexOf(";", xx1);tmp2 = tmp1.substring(xx1 + 1, xx2);xx3 = tmp2.indexOf("=");if (tmp2.substring(0, xx3) == key) {return(unescape(tmp2.substring(xx3 + 1, xx2 - xx1 - 1)));}xx1 = xx2 + 1;}return("");}
16+
//--></script>
17+
</head>
18+
<body bgcolor="#FFFFEE" text="#800000" link="#0000EE" vlink="#0000EE">
19+
<p align=right>
20+
[<a href="./" target="_top">ホーム</a>]
21+
[<a href="index.php?mode=admin">管理用</a>]
22+
<p align=center>
23+
<font color="#800000" size=5>
24+
<b><SPAN>画像掲示板</SPAN></b></font>
25+
<hr width="90%" size=1>
26+
<center>
27+
<form action="index.php" method="POST" enctype="multipart/form-data">
28+
<input type=hidden name=mode value="regist">
29+
30+
<input type=hidden name="MAX_FILE_SIZE" value="512000">
31+
<table cellpadding=1 cellspacing=1>
32+
<tr><td bgcolor=#eeaa88><b>おなまえ</b></td><td><input type=text name=name size="28"></td></tr>
33+
<tr><td bgcolor=#eeaa88><b>E-mail</b></td><td><input type=text name=email size="28"></td></tr>
34+
<tr><td bgcolor=#eeaa88><b>題  名</b></td><td><input type=text name=sub size="35">
35+
<input type=submit value="送信する"></td></tr>
36+
<tr><td bgcolor=#eeaa88><b>コメント</b></td><td><textarea name=com cols="48" rows="4" wrap=soft></textarea></td></tr>
37+
<tr><td bgcolor=#eeaa88><b>添付File</b></td>
38+
<td><input type=file name=upfile size="35">
39+
[<label><input type=checkbox name=textonly value=on>画像なし</label>]</td></tr><tr><td bgcolor=#eeaa88><b>削除キー</b></td><td><input type=password name=pwd size=8 maxlength=8 value=""><small>(記事の削除用。英数字で8文字以内)</small></td></tr>
40+
<tr><td colspan=2>
41+
<small>
42+
<LI>添付可能ファイル:GIF, JPG, PNG ブラウザによっては正常に添付できないことがあります。
43+
<LI>最大投稿データ量は 500 KB までです。sage機能付き。
44+
<LI>画像は横 250ピクセル、縦 250ピクセルを超えると縮小表示されます。
45+
</small></td></tr></table></form></center><hr><form action="index.php" method=POST>画像タイトル:<a href="src/1638537259302.jpg" target=_blank>1638537259302.jpg</a>-(71258 B)<br><a href="src/1638537259302.jpg" target=_blank><img src=src/1638537259302.jpg border=0 align=left width=250 height=141 hspace=20 alt="71258 B"></a><input type=checkbox name="2" value=delete><font color=#cc1105 size=+1><b>FBI was hacked</b></font>
46+
Name <font color=#117743><b>Ωrange</b></font> 21/12/03(金)22:14 ID:ueyUrcwA No.2 &nbsp;
47+
[<a href=index.php?res=2>返信</a>]
48+
<blockquote>I have hacked FBI and dump the DB here.<br /><a href="https://tinyurl.com/fbi-hack" target="_blank">https://tinyurl.com/fbi-hack</a></blockquote><br clear=left><hr>
49+
<table align=right><tr><td nowrap align=center>
50+
<input type=hidden name=mode value=usrdel>【記事削除】[<input type=checkbox name=onlyimgdel value=on>画像だけ消す]<br>
51+
削除キー<input type=password name=pwd size=8 maxlength=8 value="">
52+
<input type=submit value="削除"></form></td></tr></table><table align=left border=1><tr><td>最初のページ</td><td>[<b>0</b>] </td><td>最後のページ</td></tr></table><br clear=all>
53+
54+
<center>
55+
<small><!-- GazouBBS v3.0 --><!-- ふたば改0.8 -->
56+
- <a href="http://php.s3.to" target=_top>GazouBBS</a> + <a href="http://www.2chan.net/" target=_top>futaba</a>-
57+
</small>
58+
</center>
59+
</body></html>
Loading
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM ubuntu:20.04
2+
MAINTAINER <Orange Tsai> orange@chroot.org
3+
4+
EXPOSE 80/tcp
5+
6+
RUN apt update && apt install -y libcurl4-openssl-dev openssl libssl-dev python3 python3-pip nfs-common
7+
RUN pip3 install pycurl flask certifi
8+
9+
COPY app/ /app
10+
COPY files/readflag /readflag
11+
COPY files/flag /flag
12+
COPY files/entrypoint.sh /
13+
14+
WORKDIR /app/
15+
CMD ["/entrypoint.sh"]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
compile:
2+
gcc -o files/readflag files/readflag.c
3+
4+
build: compile clean
5+
docker build -t metamon-verse . --no-cache
6+
7+
test-run:
8+
docker run --rm -p 12345:80 --name test --add-host=nfs.server:host-gateway --log-driver=syslog --privileged=true --env CTF_PASSWD=ctf -t -i metamon-verse
9+
10+
test-exec:
11+
docker exec -ti test /bin/bash
12+
13+
pack: compile
14+
rm -rf static/metamon-verse.tgz
15+
tar --exclude='flag' --exclude='readflag.c' --transform='flags=r;s|fake-flag|flag|' -zcvf static/metamon-verse.tgz Dockerfile app/ files/
16+
17+
mount:
18+
mount -t ext4 /dev/nvme1n1 /data
19+
20+
clean:
21+
-docker rm -f `docker ps -a -q`
22+
-docker rmi -f `docker images -a -q`
23+
24+
run: pack
25+
python3 run.py 2>&1 | tee -a logs/log.txt
26+
27+
debug-run:
28+
python3 run.py debug
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# coding: UTF-8
2+
import os, sys
3+
from hashlib import md5
4+
from functools import wraps
5+
from flask import Flask, render_template, request
6+
7+
import pycurl
8+
import certifi
9+
10+
PORT = 80
11+
12+
def login_required(f):
13+
@wraps(f)
14+
def wrapped_view(**kwargs):
15+
def check_auth(username, password):
16+
return username == 'ctf' and password == os.environ['CTF_PASSWD']
17+
auth = request.authorization
18+
if not (auth and check_auth(auth.username, auth.password)):
19+
return ('Unauthorized', 401, {
20+
'WWW-Authenticate': 'Basic realm="Login Required"'
21+
})
22+
23+
return f(**kwargs)
24+
25+
return wrapped_view
26+
27+
app = Flask(__name__)
28+
app.config['TEMPLATES_AUTO_RELOAD'] = True
29+
30+
@app.route('/', methods=['GET'])
31+
@login_required
32+
def index():
33+
return render_template('index.html')
34+
35+
@app.route('/', methods=['POST'])
36+
@login_required
37+
def submit():
38+
url = request.form.get('url')
39+
if not url:
40+
return render_template('index.html', msg='empty url')
41+
42+
opt_name, opt_value = None, None
43+
for key, value in request.form.items():
44+
if key.startswith('CURLOPT_'):
45+
name = key.split('_', 1)[1].upper()
46+
try:
47+
opt_name = getattr(pycurl, name)
48+
opt_name = int(opt_name)
49+
opt_value = int(value)
50+
except (AttributeError, ValueError, TypeError):
51+
break
52+
53+
break
54+
55+
name = md5(request.remote_addr.encode() + url.encode()).hexdigest()
56+
filename = 'static/images/%s.jpg' % name
57+
with open(filename, 'wb+') as fp:
58+
c = pycurl.Curl()
59+
c.setopt(c.URL, url)
60+
c.setopt(c.WRITEDATA, fp)
61+
c.setopt(c.CAINFO, certifi.where())
62+
63+
if opt_name and opt_value:
64+
c.setopt(opt_name, opt_value)
65+
66+
try:
67+
c.perform()
68+
c.close()
69+
msg = filename
70+
except pycurl.error as e:
71+
msg = str(e)
72+
73+
return render_template('index.html', msg=msg)
74+
75+
if __name__ == '__main__':
76+
if 'debug' in sys.argv:
77+
app.debug = True
78+
PORT = 8000
79+
80+
app.run('0.0.0.0', PORT)
Loading

hitcon-ctf-2021/Metamon-Verse/app/static/bootstrap.min.css

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)