PlaidCTF-2019 ==== [TOC] # Crypto ## SPlaid Cypress Facts: 1. The encrypted data can be seen as a concatenation of bits. If we know the original encrypted bit lengths of each plain character, we can construct the Splay Tree by simulation. 2. The plaintext is generated by `zip plaintext.zip flag-cypress.txt danny.jpg` and `danny.jpg` is given. So we can generate the compressed data of `danny.jpg`, which should be part of `plaintext.zip`. Solution: Step 1: * Use exhaustive search that searches encrypted bit lengths of plain character. * If the Splay Tree is invalid, backtrace * The bit length is actually the height of a node in Splay Tree, which should be $O(\log N)$. Experiments show the maximum length of bits is 31. Step 2: * After the successful searching of step 1., we obtain a Splay Tree. Then we 'go back' (do anti-zigs, anti-zig-zigs, and flips) until reaching the head of known-plaintext (compressed data of danny.jpg). * Now the left child of the root should be a leave node and corresponds to the character before our known-plaintext. Step 3: * Do an exhaustive search on bit lengths again but this time the Splay Tree simulation direction is going backward. [scripts](https://github.com/david942j/ctf-writeups/tree/master/plaidctf-2019/splaid-cypress) flag: `PCTF{1t-1s-kn0wn-th4t-d4nny's-f4ce-1s-b0th-pl41n-4nd-1ntreegu1ng-s1mult4ne0usly}` ## R u SAd? We are given $iPmQ = p^{-1} \bmod q$ and $iQmP = q^{-1} \bmod p$ in the public key. So we have $p\times iPmQ + q \times iQmP = 1 \bmod n$. Then, $p\times iPmQ + q \times iQmP = 1 + k \times n$ for some very small $k$. Now we can brute-force $k$ to calculate $p \bmod iQmP$ and finally factor $n$ to get the flag. flag: `PCTF{Rub_your_hands_palm_to_palm_vigorously_for_at_least_20_seconds_to_remove_any_private_information}` ## Horst The encryption method is $x, y = y, x \times k.\text{inv}() \times y \times k$ for 3 rounds (here $x,y,k$ are permutations). Let $x_{ct},y_{ct} = enc(x_{pt},y_{pt})$. It can be simplified into $k \times x_{pt}.\text{inv}() \times x_{ct} = y_{pt} \times y_{ct} \times k$. Now we can guess any unknown value of $k$ and deduce the next number by the equation above until a cycle is formed. Brute-force all cycles to find the key. [script](https://gist.github.com/lyc12345/e8c4f4ca336aabffed7a56f745713acb/) flag: `PCTF{69f4153d282560cdaab05e14c9f1b7e0a5cc74d1}` # Pwnable ## SPlaid Birth Out-of-bound access on the `select` method. The method will splay a 'fake' node to the root and show its `index` field, it is easy to use `select` for leaking the base address of heap and libc. Construct a fake tree structure on heap such that root has a child points to `free_hook-16`, then use the `cover` method, which will push lazy tag onto children's field, to write the address of `system` to `free_hook`. [script](https://github.com/david942j/ctf-writeups/blob/master/plaidctf-2019/splaid-birch/splaid.rb) flag: `PCTF{7r335_0n_h34p5_0n_7r335_0n_5l3470r}` ## Spectre First we tested a PoC from the Internet to make sure that Spectre vulnerablity is existed on a same GCE host. Our exploit is to use `builtin_bc` to trigger speculative reading of the flag, which is located at `the_vm+0x1018`. `builtin_bc` read a byte in the code buffer at `the_vm+0x8` if the given index is less than the code size (no more than 0x1000). However, the speculative execution mechanism made it possible to out-of-bound read the flag buffer at 0x1018. Then we used the return value of `builtin_bc` as the index and accessed a non-cached array, which made the accessed element be cached. Finally, we used `builtin_time` to measure the access time of each element in the array, to find which index has been cached by the previous access. To increase the possibility that speculative execution successfully run all above instructions, we had to maximize the branch decision time, and minimize the execution time of the remaining instructions. These conditions were achieved by using large memory reading loop to flush `the_vm+0x0` (code size), and then calling `builtin_time` for caching flag buffer, since `builtin_time` read `the_vm+0x1018` and whole the cache line was fetched. ```cpp char builtin_bc(unsigned int offset) { if (the_vm->size > offset) // slow down this (non-cached) return the_vm->code[offset]; // make it fast (cached) return -1; } r11 = array[bultin_bc(0x1018)]; // immediately use the result ``` Exploit generation [script](https://gist.github.com/seanwupi/8a84f2b9964c1ed0bbce66b68993e8cc#file-spectre-gen-py) Flag: `pctf{CPU2h4rd!}` ## Suffarring ### Vulnerability In the `recant` method, heap buffer-overflow occurs when `len(needle) > len(s)`. ### Exploitation * Use the buffer-overflow to overwrite chunk's size to create overlapped chunks. * Use the overlapped chunks to leak the base address of heap and libc. * Use the overlapped chunks to free the entire `TcacheEntry` table, then forge it to have the address of `free_hook` on certain entry. * Malloc to put the address of `system` on `free_hook`. [script](https://github.com/david942j/ctf-writeups/blob/master/plaidctf-2019/suffarring/suffarring.rb) flag: `PCTF{You-hav3-suff3r3d-so-h3r3's-your-sh1ny-r1ng}` ## cppp + Vulnerability + It forgets to copy the assignment, which leads to use-after-free. + Exploitation + Create some buffer. + Use the vulnerability to leak heap and libc addresses + Tcache attack to overwrite `free_hook` with `system` + Get shell flag: `PCTF{ccccccppppppppppppPPPPP+++++!}` # Web ## Potent Quotables Fact: 1. `<>"'` is not allowed in quotes. `\0` and bad utf-8 sequence will be replaced with UTF-8 bad code point by flask template. 2. The website has CSP. 3. The cache server will only cache the `GET` requests with the response starts with `HTTP/1.1 200 OK`. 4. The flask server will not send the HTTP header when using `HTTP/0.9`. https://github.com/pallets/werkzeug/blob/d824659abe95ed31b1f9c355f88c4741da5a6e5f/src/werkzeug/serving.py#L367 Exploit: 1. we can create a quote which starts with `HTTP/1.1 200 OK` and use `GET` with `HTTP/0.9` to poison the cache server. 2. Remove the CSP header and add the `Content-Encoding: deflate` in fake header. Then, encode the payload with ASCII only deflated data to bypass character limitation by https://github.com/molnarg/ascii-zip. 3. make admin access this page via `/api/quote/<uuid>`. In this page, we don't have any CSP limitation. So just XSS admin to get the flag and send back. flag: `PCTF{TBL_sTr1k3S_4gA1n_Fr0m_199I}` ## Triggered When login, it will modify the current session to `(login_username, is_login=False)`, and when post login process, it will only update the current session with `is_login=True` without check the login_username. So we can utilize this race condition by 1. login my account and execute the post login 2. login the `admin` account in the mean while After logined to `admin` account, search PCTF of post to get the flag. flag: `PCTF{i_rAt3_p0sTgRE5_1O_oUT_0f_14_pH_n3ed5_m0Re_4Cid}` # Misc ## docker Decompress the docker image and found a file `969996089570ead17d586e6b940c8cb0375aba7bd329076cbe2a2fc18653b8d9.json` contains the flag. flag: `PCTF{well_it_isnt_many_points_what_did_you_expect}` ## Everland 1. Let our strength be zero then all enemies except the boss will always use action `lunge`. 2. Use `recouperate` to survive all battles. 3. Capture the strongest monster and sacrifice it to win the final battle. flag: `PCTF{just_be_glad_i_didnt_arm_cpt_hook_with_GADTs}` ## can you guess me Modified [pyfuck](https://github.com/wanqizhu/pyfuck) to transform any python script to only 10 chars. {%gist ShikChen/0c7ff73dee20cfc6f927a0d9824d64b8 %} flag: `PCTF{hmm_so_you_were_Able_2_g0lf_it_down?_Here_have_a_flag}` ## Space Saver There are four PNG files and one RAR file in `space_saver-90a5a93dfdda2d0333f573eb3fac9789.dd`. However, RAR file is encrypted. By using `zsteg` to check the PNG files, we can get three suspicious strings: `Spac`, `3ei2`, `herE`. The password of RAR file is `Spac3ei2herE`, and flag is inside. flag: `PCTF{2pac3_3v34ry_wh3r3}` ## graffiti flag: `PCTF{PEWPEWPEWWX}` From [here](https://github.com/OpenArena/), we can find the server and client code. ### Decode, Decrypt and Parse The packet was huffman compressed and xor encrypted. We rewrite the function PacketEvent in ``code/client/cl_main.c`` to decode & decrypt the packet data. The client challenge can be obtained from pcap file. The initial reliableCommand is null. ### Parse We also port the function CL_ParsePacketEntities() from ``code/client/cl_parse.c`` to our decoder. It will print out the detail info of snapshot. ``` ... == packet #3026 (client) == seq = 000005ac rAck: 00000002 rAck: 00000002 op: 00000007 11:playerstate 12: playerstate commandTime:65323 viewangles[1]:-144.519653 viewangles[0]:1.538086 weaponTime:47 eventSequence:273 torsoAnim:135 (269 bits) 28:packet entities 30: baseline: 126 31: #0 pos.trBase[0]:238 pos.trBase[1]:273 pos.trBase[2]:-16 eType:64 eventParm:42 (176 bits) 43: baseline: 146 43: #146 remove snapshot:0 delta:-1 ping:0 ... ``` The entitity #0 and #1023 seems like the imprint on the wall. The attribute pos.trBase 0 ~ 2 mapping to x,y and z. Extracting all the pos.trBas of entitity #0 and #1023, we got the coordinate data: ``` 299,-43,22 299,-43,22 299,-43,22 300,-44,6 300,-44,6 299,-43,3 299,-43,-1 299,-43,-5 299,-43,-9 299,-43,-13 299,-43,-16 ... ``` ### 3D Plot Upload the coordinate data to the website: https://hip70890b.de/JsPlot3D/examples/playground/index.html ![](https://i.imgur.com/D15V8B5.png) After human OCR, the flag is `PCTF{PEWPEWPEWWX}` ## A Whaley Good Joke Collect all candidate characters for each position as: ``` pctf{1_b3t[_kt][4tu][_z][_acs][lou][ku][lz][dgt]n[7_nt][07_v][cs]0[ln][kt][34n][imq][67n][38_nt][_er][_t]u[irt][2_e][_l][47_]u[_bg]h[_t][3e]r} ``` Then solve it by hands. flag: `pctf{1_b3t_u_couldnt_c0nt4in3r_ur_l4ught3r}` ## Project Eulernt Take the logarithms for every prime factor of $333!$, then solve the approximate subset sum problem with random permutation and greedy. flag: `PCTF{R3fr3sh1ngly_Sm00th}` # Reversing ## Plaid Party Planning III Use gdb to jump over the checks then the flag will be printed. flag: `PCTF{1 l1v3 1n th3 1nt3rs3ct1on of CSP and s3cur1ty and parti3s!}` ## Plaid Party Planning III 2 By reversing the binary, we need to find an order such that there's no deadlock. (There's a condition that person 6 don't like some food, which can be achieved easily by excluding some ID from that person.) For each two pair of people, enumerate all possible id for them and check whether there would be deadlock between these two people (Can be done by doing DP, but the script use a simple heuristic for this, which is actually not correct but there's no counter-example in the input data). Search through all permutations, using the above conflicts as 2-SAT to prune. This leaves us with about 5500 possible permutations. Then just simulate all candidate permutations, and see which one has output flag in ascii range. {%gist peter50216/2a836648b3ca8ef50640e8efb051830a%} flag: `PCTF{J ljv3 Jn th3 1nt3rs3ctJon of CSP and s3curjty and partj3s!}` ## big maffs After reversed the binary, we found that the program uses a strange -2 base numbering system "Negabinary". With this important discovery, we can clearly understand this program, as below. We need to calculate $\text{A}(10,10) \bmod p$. (here $\text{A}$ is the Ackermann function). We utilize the facts $\text{A}(10,10)$ would be a prime tower minus three and the modulo remainder of a prime tower is stable by Euler's formula. {%gist lyc12345/930c54bfbe3504865176f6ab79f107e7%} flag: `PCTF{u_r_a_H4CKERMANN}` ## i can count Ignore the interactive part and simply feed the checking function into angr! {%gist ShikChen/1c21fad0441f3701fbf17d5bd5dcb4de %} flag: `PCTF{2052419606511006177}` ## The .Wat ness reverse 6 constraints from WASM remote server has 5 levels only solve it manually flag: `pctf{what_if_i_made_firmament_instead}`