📜 ⬆️ ⬇️

CTFzone write-ups - MISC it all up

image


Friends, according to the tradition that has developed over the last month, we suggest you start a new week with a new raitap. In this post we will examine in detail the tasks from the MISC direction, which includes all tasks that are not suitable for any other category. There was a need for a special creative;)


Vetka MISC found a response in the soul of our players - during the competition we received about 300 flags. Note that of all tasks for 1000, the task from this category was the most popular - many were puzzled over it, but only a few people achieved success. Therefore, we decided to skip tasks for 50 and 100 points and immediately move on to more complex and interesting tasks. Go!



MISC_300. Lithium | Beta


AURORA: Lieutenant Friend, seems to be this computer. This calculator interface (nc). It was a very lazy idea to complete the compiler Design course. So he wrote a calculator in the easiest way using the simplest tools. I know that it’s quite complicated, we haven't got much time!


Decision:


Run the program and see that this is a regular calculator.


image


As follows from the legend, this program was written by a lazy developer. Most likely, this indicates that instead of parsing mathematical expressions, use simple eval.


As you can see in the screenshot, there is an error name output, but without the traces:


image


Judging by the errors, the result given to the type float is returned. If the result cannot be given, then ValueError is raised. If there is no such function, then NameError.


Let's try to find out the list of available functions by brute force.


image


However, all attempts to use underscore, for example, class, do not work because of HackingAttempt.
But using ord and str, you can calculate any variable, for example, the result dir, which returns a list of all the available names in the environment.


Brutter code:


#!/usr/bin/python2 import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("95.85.41.197", 8888)) print s.recv(1024) result = '' for i in range(0, 300): request = "ord(str(dir())[%d])" % i # request = "ord(verysecretflag[%d])" % i s.send(request) response = s.recv(32) if 'Err' in response or 'occurred' in response: result += '_' else: result += chr(int(response.split(': ')[1].split('.')[0])) print result 

The result of the search is the following list:


 ['HackingAttempt', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'e', 'f', 'inp', 'print1337', 're', 'regex', 'sys', 'verysecretflag'] 

The flag is in the verysecretflag variable. Print, it is renamed print1337
To get the answer, you should do this:


image


Here is our flag!


Answer: ctfzone {123456}


MISC_500. Archive maniac


AURORA: Oh God! Lieutenant, I need you here on the ship control station. It is a rule. It is a dead drunk. I noticed that he was concerned about storage efficiency and confidentiality. And he also preferred number 32 to 64 with no obvious reason.


Decision:


In this task, the participant is asked to find the secret code, but the difficulty lies in the fact that this key is encrypted. Unfortunately, the person who has the information will not be able to say anything intelligible in the next day ... We know that he tried to ensure reliable storage and confidentiality, and for some reason also preferred the number 32 instead of 64. Let's try to figure it out.


For a start, look at our source data. The task itself provides a link to the arch.tar.gz archive, which contains the following files:


Archive files
 [briskly@archlinux tmp]$ tar -xvf arch.tar.gz archive/ archive/flag3.png archive/flag9.png archive/flag7.png archive/flag6.png archive/flag2.png archive/flag4.png archive/flag5.png archive/flag0.png archive/flag1.png archive/flag8.png archive/.gitkeep [briskly@archlinux tmp]$ file archive/* archive/flag0.png: cpio archive archive/flag1.png: bzip2 compressed data, block size = 900k archive/flag2.png: bzip2 compressed data, block size = 900k archive/flag3.png: compress'd data 16 bits archive/flag4.png: LRZIP compressed data - version 0.6 archive/flag5.png: rzip compressed data - version 2.1 (370344 bytes) archive/flag6.png: compress'd data 16 bits archive/flag7.png: Zip archive data archive/flag8.png: ARJ archive data, v11, slash-switched, original name: , os: Unix archive/flag9.png: cpio archive 

It looks like there are 10 files in the archive with the extension .png, but each file is an archive. Judging by the file listing, the archives are very different. It's time to learn to use exotic.


Perhaps we start:


 [briskly@archlinux archive]$ bzip2 -d flag1.png bzip2: Can't guess original name for flag1.png -- using flag1.png.out [briskly@archlinux archive]$ file flag1.png.out flag1.png.out: LRZIP compressed data - version 0.6 

It seems that there are other archives in these archives. We are trying to decrypt the zip file. 7z immediately asked for a password:


 [briskly@archlinux archive]$ 7z x flag7.png 7-Zip [64] 16.02: Copyright (c) 1999-2016 Igor Pavlov: 2016-05-21 p7zip Version 16.02 (locale=ru_RU.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs Intel(R) Core(TM) i7-6600U CPU @ 2.60GHz (406E3),ASM,AES-NI) Scanning the drive for archives: 1 file, 385755 bytes (377 KiB) Extracting archive: flag7.png -- Path = flag7.png Type = zip Physical Size = 385755 Enter password (will not be echoed): 

In this case, it is worth automating the process of extracting from the archive, since there are a lot of them:


Extract from archive
 #!/bin/bash FILE="$1" FLAG="flag.png" TMP_DIR="PNGs" # Dirty: [ ! -d "./$TMP_DIR" ] && mkdir "$TMP_DIR" && echo -e "\n [+] Creating temp folder: $TMP_DIR." [ ! -f "./$FLAG" ] && cp $FILE flag.png && echo -e " [+] Creating temp file: $FLAG.\n" deArch () { CHECK=`file "$FLAG"` if [[ $CHECK == *"rzip compressed data"* ]] then echo -e " [*] Now $FLAG is RZIP data (.rz)\n [+] Extracting $FLAG\n" mv flag.png{,.rz} runzip -d flag.png.rz #rm flag.png.rz sleep 1 deArch elif [[ $CHECK == *"LRZIP compressed data"* ]] then echo -e " [*] Now $FLAG is LRZIP archive (.lrz)\n [+] Extracting $FLAG\n" mv flag.png{,.lrz} lrunzip flag.png.lrz > /dev/null rm flag.png.lrz sleep 1 deArch elif [[ $CHECK == *"bzip2 compressed data"* ]] then echo -e " [*] Now $FLAG is BZIP file (.bz2)\n [+] Extracting $FLAG\n" mv flag.png{,.bz2} bzip2 -d flag.png.bz2 #> /dev/null sleep 1 deArch elif [[ $CHECK == *"compress'd data 16 bits"* ]] then echo -e " [*] Now $FLAG is unix compressed file (.z)\n [+] Extracting $FLAG\n" mv flag.png{,.z} uncompress flag.png.z sleep 1 deArch elif [[ $CHECK == *"7-zip archive data"* ]] then echo -e " [*] Now $FLAG is 7-ZIP archive (.7z)\n [+] Extracting $FLAG\n" mv flag.png{,.7z} 7z x flag.png.7z > /dev/null rm flag.png.7z sleep 1 deArch elif [[ $CHECK == *"ARJ archive data, v11, slash-switched"* ]] then echo -e " [*] Now $FLAG is ARJ archive (.arj)\n [+] Extracting $FLAG\n" mv flag.png{,.arj} arj x flag.png.arj > /dev/null rm flag.png.arj sleep 1 deArch elif [[ $CHECK == *"cpio archive"* ]] then echo -e " [*] Now $FLAG is CPIO archive (.cpio)\n [+] Extracting $FLAG\n" mv flag.png{,.cpio} cpio -idv < flag.png.cpio 2> /dev/null rm flag.png.cpio sleep 1 deArch elif [[ $CHECK == *"current ar archive"* ]] then echo -e " [*] Now $FLAG is AR archive (.a)\n [+] Extracting $FLAG\n" mv flag.png{,.a} ar x flag.png.a rm flag.png.a sleep 1 deArch elif [[ $CHECK == *"Zip archive data"* ]] then echo -e " [*] Now $FLAG is zip archive (.zip)\n [+] Extracting $FLAG\n" mv flag.png{,.zip} #mv flag.png{,.7z} ENC_CHCK=`7z l -slt -- flag.png.zip | grep -ic "Encrypted = +"` if [ "$ENC_CHCK" -eq "1" ] then #exit 1 echo " [!] PASSWORD pretocted archive" zip2john flag.png.zip | awk -F: '{print $2}' > hash.lst rm -rf /root/.john/john.* #ZIP_PASS=`john hash.lst 2>&1 > /dev/null | awk '/\(\?\)/ {print $1}'` ZIP_PASS=`john hash.lst --wordlist=/usr/share/wordlists/rockyou.txt 2>&1 | awk '/\(\?\)/ {print $1}'` if [[ -z "$ZIP_PASS" ]] then #echo -e "$ZIP_PASS" echo -e " [-] Your pass was not found, please, try it manually..." else echo -e " [+] Voila! Your pass is: \e[1;33m$ZIP_PASS\e[0;0m, extracting an archive...\n" 7z x -p"$ZIP_PASS" flag.png.zip > /dev/null rm flag.png.zip sleep 1 deArch fi else 7z x flag.png.zip sleep 1 deArch fi #sleep 1 #deArch elif [[ $CHECK == *"PNG image data"* ]] then echo -e " [\e[1;32m*\e[0;0m] Now $FLAG is PNG image file !!!\n [\e[1;32m+\e[0;0m] Open this: ./$TMP_DIR/$FILE\n" sleep 1 #eog flag.png 2> /dev/null mv $FLAG $TMP_DIR/$FILE exit 0 else echo -e "\n [-] Hernya! $CHECK" fi } deArch 

The result of the script:


Result
 bash deArch.sh flag4.png [+] Creating temp folder: PNGs. [+] Creating temp file: flag.png. [*] Now flag.png is ARJ archive (.arj) [+] Extracting flag.png [*] Now flag.png is zip archive (.zip) [+] Extracting flag.png [!] PASSWORD pretocted archive [+] Voila! Your pass is: love123, extracting an archive... [*] Now flag.png is unix compressed file (.z) [+] Extracting flag.png [*] Now flag.png is AR archive (.a) [+] Extracting flag.png [*] Now flag.png is BZIP file (.bz2) [+] Extracting flag.png [*] Now flag.png is CPIO archive (.cpio) [+] Extracting flag.png [*] Now flag.png is RZIP data (.rz) [+] Extracting flag.png [*] Now flag.png is 7-ZIP archive (.7z) [+] Extracting flag.png [*] Now flag.png is LRZIP archive (.lrz) [+] Extracting flag.png [*] Now flag.png is PNG image file !!! [+] Open this: ./PNGs/flag4.png 

As a result, flag4.png is pulled out of the file.


image


So, the picture is received! Let's see what's inside - maybe there is a steganography?
To do this, run Stegsolve, and by changing some settings, we’ll get something quite legible:


image


Judging by the signs =, it looks like base64. But all capital letters (uppercase). Let us recall the legend, where it is said that the person who possessed knowledge preferred the number 32. Let's try base32, as a result we get TPAU'XAPDEP.


We carry out the same actions in relation to other files.


As a result, we obtain the following:


 thisnotaflag thisisflag,joke noflaghere noo000000op CTFZONE{5dbb39d62d31b1c notflagagain flagwashere 025f3b0e3a987d375}part2 kakoyflag? TPAU'XAPDEP 

Here is our flag!


Answer: ctfzone {5dbb39d62d31b1c025f3b0e3a987d375} part2


MISC_1000. Molibden | Gamma


AURORA: Lieutenant, you've got to the command center. It's time to go home and join our comrades! Wait, something is wrong with the systems. Some basic libraries are lost. Computer can't find the route. Make some simple calculations. Quick, we are almost there!


Decision:


So, we are almost there! To get control of the ship, you need to correct the error in the system.


So, run the program. An example of the program we see in the screenshot.


image


From the legend it is clear that we need to interpret the code that the server sends. To get a solution you will have to show your programming skills.


Since the code is written in Python, the first obvious solution is to do exec and send the result back. The code will look like this:


 from socket import create_connection from time import time sock = create_connection(("95.85.41.197", 8887)) for i in range(10): res = None code = sock.recv(102400) code = code.decode() code = "\n".join(code.split("\n")[:-2]) if "gone wrong" in code: print(code) exit(0) start = time() l = res = None print(code) exec(code) res = res or l print(time() - start) code = None res = str(res).encode() try: sock.send(res + b"\n") except Exception: print(sock.recv(102400)) print(sock.recv(102400)) print(sock.recv(102400)) break 

After that comes the code with sleep, which slows down the execution:


 from time import sleep k = 96 s = 36 c = 98 mas = [] for i in range(c): sleep(0.1) mas.append(s) s += k res = 0 for el in mas: sleep(0.1) res += el print(res) 

This problem is solved simply by cutting out sleep on a regular basis. For example, like this:


 code = code.replace("sleep(0.1)", "") 

The following complication is that sleep is renamed upon import:


 from time import sleep as JecYvyk 

In this case, you can write a regular-schedule or simply replace:


 code = code.replace("sleep", "gmtime") 

The next time the solution stops at a step with a code that uses large numbers:


 from time import gmtime as rluVx k = 82194181 s = 55474764 c = 54888629 mas = [] for i in range(c): rluVx(2*0.01) mas.append(s) s += k res = 0 for el in mas: rluVx(2*0.01) res += el print(res) 

At this stage, the program is not executed for two reasons - either the memory is running low or the server gives an error:


 Something gone wrong: TimeoutError 

Obviously, just to solve this problem will not work, you will have to understand the code. When you first evaluate the code, you will notice that this is very similar to the sum of an arithmetic progression.


Since the function is really inefficiently written, instead of the usual linear formula, an array with all elements of an arithmetic progression is formed, and then it is added. We write this formula:


 def solve(k, s, c): return str((2*s + k*(c-1))*c//2) 

It looks pretty simple.


The next problem is that the code stops running:


 from time import sleep as Rkyv from random import shuffle l = [5984807, 6299947, 10119240, 13578507, 14224900, 15238270, 15513380, 16429758] while True: Rkyv(0o10*0.01) shuffle(l) prev = None is_sorted = True for el in l: Rkyv(0o10*0.01) if prev is None: prev = el elif prev >= el: is_sorted = False break prev = el if is_sorted: break print(l) 

We'll have to deal with this task. Very similar to monkey_sort, but, apparently, this code cannot be executed. Rewrite to the usual sort, which provides us with Python. But this is not enough.


As a result, there comes a time when a regular exec no longer has time to read the 'scary' obfuscated code:


Scary obfuscated code
 from time import sleep as EB s = b'Vt\xe9\xe9\xed\x05J\xdaEQ' def func3(s): def func1(OOOOOOOOO0000OOO0 ): "" PI_SUBST =[41 ,46 ,67 ,201 ,162 ,216 ,124 ,1 ,61 ,54 ,84 ,161 ,236 ,240 ,6 ,19 ,98 ,167 ,5 ,243 ,192 ,199 ,115 ,140 ,152 ,147 ,43 ,217 ,188 ,76 ,130 ,202 ,30 ,155 ,87 ,60 ,253 ,212 ,224 ,22 ,103 ,66 ,111 ,24 ,138 ,23 ,229 ,18 ,190 ,78 ,196 ,214 ,218 ,158 ,222 ,73 ,160 ,251 ,245 ,142 ,187 ,47 ,238 ,122 ,169 ,104 ,121 ,145 ,21 ,178 ,7 ,63 ,148 ,194 ,16 ,137 ,11 ,34 ,95 ,33 ,128 ,127 ,93 ,154 ,90 ,144 ,50 ,39 ,53 ,62 ,204 ,231 ,191 ,247 ,151 ,3 ,255 ,25 ,48 ,179 ,72 ,165 ,181 ,209 ,215 ,94 ,146 ,42 ,172 ,86 ,170 ,198 ,79 ,184 ,56 ,210 ,150 ,164 ,125 ,182 ,118 ,252 ,107 ,226 ,156 ,116 ,4 ,241 ,69 ,157 ,112 ,89 ,100 ,113 ,135 ,32 ,134 ,91 ,207 ,101 ,230 ,45 ,168 ,2 ,27 ,96 ,37 ,173 ,174 ,176 ,185 ,246 ,28 ,70 ,97 ,105 ,52 ,64 ,126 ,15 ,85 ,71 ,163 ,35 ,221 ,81 ,175 ,58 ,195 ,92 ,249 ,206 ,186 ,197 ,234 ,38 ,44 ,83 ,13 ,110 ,133 ,40 ,132 ,9 ,211 ,223 ,205 ,244 ,65 ,129 ,77 ,82 ,106 ,220 ,55 ,200 ,108 ,193 ,171 ,250 ,36 ,225 ,123 ,8 ,12 ,189 ,177 ,74 ,120 ,136 ,149 ,139 ,227 ,99 ,232 ,109 ,233 ,203 ,213 ,254 ,59 ,0 ,29 ,57 ,242 ,239 ,183 ,14 ,102 ,88 ,208 ,228 ,166 ,119 ,114 ,248 ,235 ,117 ,75 ,10 ,49 ,68 ,80 ,180 ,143 ,237 ,31 ,26 ,219 ,153 ,141 ,51 ,159 ,17 ,131 ,20 ] O00OOOO0OOOO00000 =OOOOOOOOO0000OOO0 OO00OO000OO0OO000 =len (O00OOOO0OOOO00000 ) O00OOOO0OOOO00000 +=chr (16 -(OO00OO000OO0OO000 %16 )).encode ("utf-8")*(16 -(OO00OO000OO0OO000 %16 )) O00OOOO0O0OOO00O0 =O00OOOO0OOOO00000 OO00OO000OO0OO000 =len (O00OOOO0OOOO00000 ) OOOOO0O0000000OO0 =bytearray (b"\x00"*16 ) OO0O0O000OOOO0O0O =0 for O0OOO0OO000OO00O0 in range (OO00OO000OO0OO000 //16 ): EB(0x3*0.01) for OO00O00OOO000OO00 in range (16 ): EB(0x3*0.01) O0O0OOOO000O0OO0O =O00OOOO0O0OOO00O0 [O0OOO0OO000OO00O0 *16 +OO00O00OOO000OO00 ] OOOOO0O0000000OO0 [OO00O00OOO000OO00 ]=OOOOO0O0000000OO0 [OO00O00OOO000OO00 ]^PI_SUBST [O0O0OOOO000O0OO0O ^OO0O0O000OOOO0O0O ] OO0O0O000OOOO0O0O =OOOOO0O0000000OO0 [OO00O00OOO000OO00 ] OO0OOO00OOO00000O =O00OOOO0O0OOO00O0 +OOOOO0O0000000OO0 OO00OO000OO0OO000 +=16 OOO00O00O0O0O0OO0 =bytearray ([0 ])*48 for O0OOO0OO000OO00O0 in range (OO00OO000OO0OO000 //16 ): for OO00O00OOO000OO00 in range (16 ): EB(0x3*0.01) OOO00O00O0O0O0OO0 [16 +OO00O00OOO000OO00 ]=OO0OOO00OOO00000O [O0OOO0OO000OO00O0 *16 +OO00O00OOO000OO00 ] OOO00O00O0O0O0OO0 [32 +OO00O00OOO000OO00 ]=OOO00O00O0O0O0OO0 [16 +OO00O00OOO000OO00 ]^OOO00O00O0O0O0OO0 [OO00O00OOO000OO00 ] OOOO0O0OO000000OO =0 for OO00O00OOO000OO00 in range (18 ): EB(0x3*0.01) for OOOO00O00OOO0OOOO in range (48 ): EB(0x3*0.01) OOOO0O0OO000000OO =OOO00O00O0O0O0OO0 [OOOO00O00OOO0OOOO ]=OOO00O00O0O0O0OO0 [OOOO00O00OOO0OOOO ]^PI_SUBST [OOOO0O0OO000000OO ] OOOO0O0OO000000OO =(OOOO0O0OO000000OO +OO00O00OOO000OO00 )%256 return bytes (OOO00O00O0O0O0OO0 [:16 ]) OO0OOOO00OO0O0O0O = b'Vt\xe9\xe9\xed\x05J\xdaEQ' def func2(OO0OOOO00OO0O0O0O): O00O00OO000OOO00O ='0123456789abcdef' return b''.join(map(lambda x: x.encode(), map(lambda O00O0OO0O0OOOO0O0 :O00O00OO000OOO00O [(O00O0OO0O0OOOO0O0 >>4 )&0xf ]+O00O00OO000OOO00O [O00O0OO0O0OOOO0O0 &0xf ],func1 (OO0OOOO00OO0O0O0O )))) for i in range(100): OO0OOOO00OO0O0O0O = func2(OO0OOOO00OO0O0O0O) return OO0OOOO00OO0O0O0O.decode() #print(''.join (map (lambda O00O0OO0O0OOOO0O0 :O00O00OO000OOO00O [(O00O0OO0O0OOOO0O0 >>4 )&0xf ]+O00O00OO000OOO00O [O00O0OO0O0OOOO0O0 &0xf ],func1 (OO0OOOO00OO0O0O0O )))) res = func3(s) print(res) 

In the course of a small deobfuscation, it becomes clear that this is most likely some kind of hash function that is applied 100 times in succession. Then you can go in two ways: either simply rewrite this function on something faster (for example, C ++), or try to look for the replacement table that is hardcoded in the code.


Let's google it:


image


It's easy enough to guess that this is md2. PYCRYPTO has a quick implementation of this feature:


 from Crypto.Hash import MD2 def solve(inp): for i in range(params.get("count")): h = MD2.new() h.update(inp) inp = h.hexdigest() return inp 

Resulting solver code:


Code solver
 import re import json from socket import create_connection from solves import md2 from solves import arifmetic from time import time sock = create_connection(("95.85.41.197", 8887)) r1 = re.compile(r"l = (\[.*\])") r21 = re.compile(r"OO0OOOO00OO0O0O0O = (b('|\").*('|\"))\n") r22 = re.compile(r" for i in range\((\d+)\):") r3 = re.compile(r".*k = (?P<k>\d+)\ns = (?P<s>\d+)\nc = (?P<c>\d+).*") def solve1(code): reverse = True if ">" in code: reverse = False code = code.split("while True:")[0] lst = r1.findall(code)[0] lst = json.loads(lst) lst.sort(reverse=reverse) return lst def solve2(code): data = eval((r21.findall(code))[0][0]) count = int(r22.findall(code)[0]) res = md2.solve({"string": data, "count": count}) return res def solve3(code): for m in r3.finditer(code): print (arifmetic.solve({k: int(v) for k, v in m.groupdict().items()})) return arifmetic.solve({k: int(v) for k, v in m.groupdict().items()}) while True: res = None code = sock.recv(102400) code = code.decode() code = "\n".join(code.split("\n")[:-2]) code = code.replace("sleep(0.1)", "") if "gone wrong" in code: print(code) exit(0) start = time() if "shuffle" in code: res = solve1(code) elif "OO0OOOO00OO0O0O0O" in code: res = solve2(code) elif "mas.append" in code: res = solve3(code) else: print("EXECING") print(code) exec(code) print(sock.recv(102400)) print(sock.recv(102400)) print(sock.recv(102400)) print(sock.recv(102400)) exit(0) print(time()-start) code = None res = str(res).encode() try: sock.send(res + b"\n") except Exception: print(sock.recv(102400)) print(sock.recv(102400)) break 

As a result, we get the long-awaited flag!


image


Answer: ctfzone {YouRealyHaveSoMuchTime?}


By the way, according to your numerous requests, we posted offline tasks - now you can play around with raitaps on this portal . But do not forget about our hiring tasks , they will be available for another 10 days until December 15th - there is still time!


If you have any questions - leave comments and write to our chat in Telegram . Nothing is as inspiring as your activity :)


All good and good luck!


')

Source: https://habr.com/ru/post/316846/


All Articles