{"id":20703,"date":"2016-10-06T17:00:19","date_gmt":"2016-10-07T00:00:19","guid":{"rendered":"https:\/\/www.paloaltonetworks.com\/blog\/?p=20703"},"modified":"2016-12-09T10:55:56","modified_gmt":"2016-12-09T18:55:56","slug":"unit42-labyrenth-capture-flag-ctf-random-track-solutions","status":"publish","type":"post","link":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/2016\/10\/unit42-labyrenth-capture-flag-ctf-random-track-solutions\/","title":{"rendered":"LabyREnth Capture the Flag (CTF): Random Track Solutions"},"content":{"rendered":"<p>Welcome back to our blog series where we reveal the\u00a0solutions\u00a0to\u00a0<a href=\"https:\/\/www.paloaltonetworks.com\/blog\/2016\/07\/unit42-announcing-the-labyrenth-capture-the-flag-ctf-challenge\/\" target=\"_blank\">LabyREnth, the Unit 42 Capture the Flag (CTF)<\/a>\u00a0challenge. Over the last several weeks, we revealed the\u00a0solutions\u00a0for each of the challenge tracks. The time has come for us to share the solutions to our last track, the Random track.<!--more--><\/p>\n<h3>Random 1 Challenge: OMG Java<\/h3>\n<p><em>Challenge Created By: Jacob Soo <a href=\"https:\/\/twitter.com\/_jsoo_\" rel=\"nofollow,noopener\"  target=\"_blank\">@_jsoo_<\/a> <\/em><\/p>\n<p>We are given a Java file that we can decompile with ByteCodeViewer.<\/p>\n<p><a href=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-1.png\"><div style=\"max-width:100%\" data-width=\"975\"><span class=\"ar-custom\" style=\"padding-bottom:54.46%;\"><img loading=\"lazy\" decoding=\"async\"  class=\"alignnone wp-image-20706 size-full lozad\"  data-src=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-1.png\" alt=\"labyrenth_random-1\" width=\"975\" height=\"531\" \/><\/span><\/div><\/a><\/p>\n<p>We can see that we need an environment variable Admin and there is an if condition (if \u2013isDrunk) in the contents of the variable. If we run it with that variable, we get the following output.<\/p>\n<pre class=\"lang:default decode:true\">env Admin=\"Tyler-isDrunk\" java omg\r\nK5SSA2DPOBSSA6LPOUQGK3TKN54SA5DINFZSASTBOZQSAYLQOAQGC4ZAO5QXE3JNOVYC4ICUNBSSA4TFON2CA53JNRWCAYTFEBWXKY3IEBWW64TFEBZWK6DDNF2GS3THEEQFI2DFEBTGYYLHEBUXGICQIFHHWRBQL5MTA5K7IV3DG3S7IJQXGZJTGJ6Q\u00cd\u00cd\u00cd\u00cd\r\ndropped flag: K5SSA2DPOBSSA6LPOUQGK3TKN54SA5DINFZSASTBOZQSAYLQOAQGC4ZAO5QXE3JNOVYC4ICUNBSSA4TFON2CA53JNRWCAYTFEBWXKY3IEBWW64TFEBZWK6DDNF2GS3THEEQFI2DFEBTGYYLHEBUXGICQIFHHWRBQL5MTA5K7IV3DG3S7IJQXGZJTGJ6Q\u00cd\u00cd\u00cd\u00cd\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>If we base32 decode the output, we get the flag.<\/p>\n<p>We hope you enjoy this Java app as a warm-up. The rest will be much more exciting! The flag is PAN{D0_Y0u_Ev3n_Base32}<\/p>\n<h3>Random 2 Challenge: Can you express yourself regularly?<\/h3>\n<p><em>Challenge Created By: Richard Wartell <a href=\"https:\/\/twitter.com\/wartortell\" rel=\"nofollow,noopener\"  target=\"_blank\">@wartortell<\/a> <\/em><\/p>\n<p>For this challenge, we\u2019re handed two files: server.py and omglob_what_is_dis_crap.txt. Taking a quick look at server.py, it looks like this is a server running somewhere, accepting connections, checking a regex, and then returning a key if the passed information doesn\u2019t match the regex.<\/p>\n<pre class=\"lang:default decode:true\">class DoAThing(SocketServer.StreamRequestHandler):\r\n    def handle(self):\r\n        self.request.sendall(\"Enter your key:\\n\")\r\n        msg = self.rfile.readline().strip()\r\n\r\n        if not r.match(msg):\r\n            self.request.sendall(\"Correct key. Here's all the passwords: %s\\n\" % FLAG)\r\n        else:\r\n            self.request.sendall(\"Failure...\\n\")\r\n<\/pre>\n<p>So what\u2019s the regex that it tests against? Well, we see that it reads in the string from omglob_what_is_dis_crap.txt. So let\u2019s take a look at that\u2026<\/p>\n<pre class=\"lang:default decode:true\">^(.*[^0mglo8sc1enC3].*|.{,190}|.{192,}|.{97}[cgClm]|.{135}[so1l803]|.{81}[e]|.{81}[c3nl8]|.{102}[e]|.{58}[cos]|.{129}[meCln3sc]|.{132}[Cslco]|.{77}[1]|.{173}[l1e30cmg8]|.{166}[m0nle]|.{158}[sngl03c81m]|.{140}[o]|.{68}[g]|.{121}[e1]|.{121}[m8cn]|.{53}[18]|.{123}[1]|.{167}[s]|.{2}[C]|.{171}[c1ms0]|.{76}[03ge]|.{108}[nm8ge301C]|.{11}[0Cec8s3on]|.{50}[18]|.{56}[c]|.{37}[3l1n0]|.{166}[c3]|.{20}[g1]|.{6}[C]|.{115}[13cnm8eg]|.{95}[mo]|.{133}[Cmo0l]|.{53}[0osCm3]|.{147}[gnlme3C81c]|.{18}[3Clcom0n8]|.{154}[egmsCnc8lo]|.{54}[g]|.{144}[1]|.{43}[3Clnec8]|.{138}[3c1sl]|.{104}[0oe]|.{45}[l0C8]|.{103}[c38ol]|.{16}[oc3nC8e0l]|.{31}[C]|.{105}[1col3]|.{150}[g1]|.{124}[38]|.{78}[cmel0sCn1g]|.{183}[m8co30s]|.{145}[o]|.{49}[o1e03sC8]|.{85}[sn]|.{2}[e]|.{150}[moe]|.{57}[le]|.{17}[ne8sm3]|.{185}[om]|.{115}[l]|.{102}[om3Cc8]|.{40}[cnl8gCm]|.{189}[3m1gle]|.{137}[es]|.{178}[C13msneo0g8]|.{136}[C]|.{37}[og]|.{100}[m]|.{64}[mo8]|.{41}[g]|.{87}[3]|.{57}[g]|.{27}[sncl]|.{157}[1emn]|.{55}[83lmson0g1ce]|.{29}[slmcn]|.{53}[l]|.{22}[lgoc]|.{113}[3]|.{132}[83]|.{164}[el3o]|.{148}[eC]|.{30}[m]|.{152}[g]|.{49}[m]|.{87}[l]|.{44}[e1038nolmcC]|.{47}[ec1sngom]|.{90}[ln8o0c]|.{180}[ln0em3oCc1g8]|.{138}[8]|.{178}[c]|.{30}[s3l]|.{73}[c]|.{1}[en]|.{161}[e0g]|.{114}[1g0es8n]|.{154}[0]|.{37}[sec]|.{166}[1C8]<\/pre>\n<p>From this regex we can see three different types of conditions:<\/p>\n<ol>\n<li><strong>.*[^0mglo8sc1enC3].*<br \/>\n<\/strong>This condition tells us that the regex will match on any string containing characters other than \u201c0mglo8sc1enC3\u201d<\/li>\n<li><strong>.{,190}|.{192,}<br \/>\n<\/strong>This condition tells us that the regex will match on any string that is not 191 characters long, giving us the correct key length.<\/li>\n<li><strong>.{97}[cgClm]<br \/>\n<\/strong>The rest of the conditions look like this one. Each of them gives us a position in the key string, and tells us characters that that position will match on. Essentially, each one is telling us what characters don\u2019t work at certain positions.<\/li>\n<\/ol>\n<p>So, we need to create a string that is 191 characters long, and use the third type of condition to tell what characters each position can be. We can create a python script that parses the regex and gets out all of the appropriate conditions, then creates a string for us that won\u2019t match those conditions. So feast your eyes below on a regex to parse a regex.<\/p>\n<pre class=\"lang:default decode:true \">import re\r\n\r\nwith open(\"omglob_what_is_dis_crap.txt\", \"r\") as f:\r\n    r = f.read()\r\n\r\nstuff = re.findall(r\"\\.\\{(\\d+)\\}\\[(\\w+)\\]\", r)\r\nsolution = [list('0mglo8sc1enC3')] * 191\r\n\r\nfor pos, s in stuff:\r\n    solution[int(pos)] = list(set(solution[int(pos)]) - set(list(s)))\r\nprint \"\".join(map(str, [item[0] for item in solution]))\r\n<\/pre>\n<p>When we run this, we get the following output:<\/p>\n<pre class=\"lang:default decode:true \">gg0ssgccCn8ggs83sggC01n8lecgs311eocc0m0n3C81gmoC1nm1Cn0Cs1g330sgnc0mCc18l18Cco8em0mC80m88csogceemoClCl00nC3gocn0egconsCcm3Clmeglo1lensgmsgnssslels10sglom31mlleg0le80csmnC033nm8oglgs0cellgmmns<\/pre>\n<p>We can now test this regex to see if it works with the server and it will give us the key:<\/p>\n<p><a href=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/Key_1.png\"><div style=\"max-width:100%\" data-width=\"974\"><span class=\"ar-custom\" style=\"padding-bottom:15.4%;\"><img loading=\"lazy\" decoding=\"async\"  class=\"alignnone size-full wp-image-20754 lozad\"  data-src=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/Key_1.png\" alt=\"key_1\" width=\"974\" height=\"150\" \/><\/span><\/div><\/a><\/p>\n<p>Which returns the key to get us to the next level:<\/p>\n<p>PAN{th4t5_4_pr311y_dum8_w4y_10_us3_r3g3x}<\/p>\n<h3>Random 3 Challenge: I'm not sure how they got in or what they were after but they left some cookie crumbs.<\/h3>\n<p><em>Challenge Created By: Anthony Kasza <a href=\"https:\/\/twitter.com\/anthonykasza\" rel=\"nofollow,noopener\" >@anthonykasza<\/a> <\/em><\/p>\n<p>Opening the provided network trace file with Wireshark and observing the IPv4 conversations, participants found only two systems communicating in the pcap. By observing the TCP tab of the conversations menu, participants could see the pcap file contains <strong>many<\/strong> connections from a single source port to almost all destination ports. This is indicative of a port scan.<\/p>\n<p>This hypothesis can be confirmed by looking at the content of the TCP connections; there is none. Each SYN packet (except for 7) is responded to with an RST. The remaining seven connections, which responded with a SYN+ACK, were immediately closed with a RST by the originating side of the connection. This pcap definitely contains a TCP SYN port scan.<\/p>\n<p>From this point, participants were required to hunt around in the pcap for the next step of the challenge. The only thing that changes between SYN packets in the trace file are:<\/p>\n<ul>\n<li>Destination port numbers<\/li>\n<li>Sequence numbers<\/li>\n<li>TCP checksums<\/li>\n<\/ul>\n<p>By observing the first SYN packet of the capture file, participants could locate a zip file header (0x504b0304) within the sequence number. I also <a href=\"https:\/\/twitter.com\/anthonykasza\/status\/755560856029696005\" rel=\"nofollow,noopener\" >tweeted a hint<\/a> for this challenge referencing DoS cookies, a <a href=\"https:\/\/en.wikipedia.org\/wiki\/SYN_cookies\" rel=\"nofollow,noopener\" >technology used to prevent SYN floods<\/a>.<\/p>\n<p>Below is a picture of the zip header in the first packet\u2019s sequence number of the trace file.<\/p>\n<p><a href=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-3.png\"><div style=\"max-width:100%\" data-width=\"900\"><span class=\"ar-custom\" style=\"padding-bottom:51.78%;\"><img loading=\"lazy\" decoding=\"async\"  class=\"alignnone wp-image-20712 lozad\"  data-src=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-3.png\" alt=\"labyrenth_random-3\" width=\"900\" height=\"466\" \/><\/span><\/div><\/a><\/p>\n<p>A Python script using dpkt could easily be written to extract and reassemble the zip from the sequence numbers of the SYN packets.<\/p>\n<p>Upon extracting the contents of the extracted zip file, participants find 853 numbered files. Opening and observing the contents of the files, participants should have been able to recognize base64 encoding. The files are each \u201cchunks\u201d of a base64 encoded file. By grepping the files for \u2018==\u2019 participants could find the last \u201cchunk\u201d, 339.bin. Then, grepping for the beginning contents of 339.bin, \u201cSBAW\u201d, participants could identify a file which overlapped with 339.bin, 531.bin. Participants could continue this manual process and reconstruct the original base64 blob using mad copy and paste skills. Or, a <a href=\"https:\/\/gist.github.com\/anthonykasza\/19db4d5269b7427e1eefd7d885325a77\" rel=\"nofollow,noopener\" >script like this<\/a> could have been used.<\/p>\n<p>Decoding the base64 blob results in another zip file which contains four images. One of the images contains the flag, PAN{YouDiD4iT.GREATjob}.<\/p>\n<h3>Random 4 Challenge: And you thought you hated PHP before you started this challenge<\/h3>\n<p><em>Challenge Created By: Josh Grunzweig <a href=\"https:\/\/twitter.com\/jgrunzweig\" rel=\"nofollow,noopener\" >@jgrunzweig<\/a> <\/em><\/p>\n<p>For the fourth challenge in the Random track, users are presented with a PHP script. This particular script weighs in around 1500 lines and presents a user with a text-based maze-like game where they must appropriately choose the correct path to receive the answer.<\/p>\n<p><a href=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-4.png\"><div style=\"max-width:100%\" data-width=\"500\"><span class=\"ar-custom\" style=\"padding-bottom:183%;\"><img loading=\"lazy\" decoding=\"async\"  class=\"aligncenter size-large wp-image-20715 lozad\"  data-src=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-4.png\" alt=\"labyrenth_random-4\" width=\"500\" height=\"915\" \/><\/span><\/div><\/a><\/p>\n<p>Looking at the underlying code, we see a large blob of obfuscated PHP code, followed by the HTML that generates the data above. The code consists primarily of what looks to be junk code, along with a few lines of unique code scattered within that to actually perform tasks.<\/p>\n<p><a href=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-5.png\"><div style=\"max-width:100%\" data-width=\"500\"><span class=\"ar-custom\" style=\"padding-bottom:68.8%;\"><img loading=\"lazy\" decoding=\"async\"  class=\"aligncenter size-large wp-image-20718 lozad\"  data-src=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-5.png\" alt=\"labyrenth_random-5\" width=\"500\" height=\"344\" \/><\/span><\/div><\/a><\/p>\n<p>Making a copy of this file and removing this junk code provides us with the following:<\/p>\n<pre class=\"lang:default decode:true \">&lt;?php\r\n\r\n$b99=idal(array(238,239,235,238,164));\r\n$l8d4345=idal(array(168,172,177,173,174,165,164));\r\n$z83=idal(array(160,179,179,160,184,158,167,168,173,181,164,179));\r\n$tf082308=idal(array(164,183,160,173,233,166,187,168,175,167,173,160,181,164,233));\r\n$h2bbd60=idal(array(232,232,250));\r\n$z61=idal(array(164,183,160,173));\r\n$nfa8b0b=idal(array(178,180,163,178,181,179));\r\n$x1418=idal(array(229));\r\n$e7r27=idal(array(177,179,164,166,158,179,164,177,173,160,162,164));\r\n$kx3=idal(array(166,164,181,158,165,164,167,168,175,164,165,158,167,180,175,162,181,168,174,175,178));\r\n$l84=$kx3();\r\n$d7f=idal(array(177,160,162,170));\r\n$tc2e980=idal(array(166,187,168,175,167,173,160,181,164));\r\n$mzoha=null;\r\n$l84=$z83($l84[idal(array(180,178,164,179))], idal(array(177,248,247,240,247,246,163)));\r\n$p6a = array();\r\nforeach($l84 as $f){$p6a[] = $nfa8b0b($f,5);};\r\n$ke03 = $d7f(idal(array(137,235)),$l8d4345($mzoha,$p6a));\r\n$r80 = idal(array(170,164,241,242));\r\n$e7r27($b99, $tf082308.$x1418.$r80.$h2bbd60, $mzoha);\r\nfunction p96167b($zwaxa){global $nfa8b0b; return ($nfa8b0b($zwaxa,0,5)== idal(array(163,245,164,163,165)));}\r\nfunction idal($zwaxa){global $mzoha; $qoazis34 = $mzoha;for($i=0; $i &lt; count($zwaxa); $i++)$qoazis34.=chr($zwaxa[$i]) ^ chr(193);return $qoazis34;}\r\n\r\n?&gt;\r\n<\/pre>\n<p>After de-obfuscating the code above, we get a better understanding of what is going on. Comments have been added and the code has been better formatted to show what everything is doing.<\/p>\n<pre class=\"lang:default decode:true \">&lt;?php\r\n\r\n\/\/ Decoded: '\/.*\/e'\r\n$b99=idal(array(238,239,235,238,164));\r\n\r\n\/\/ Decoded: 'implode'\r\n$l8d4345=idal(array(168,172,177,173,174,165,164));\r\n\r\n\/\/ Decoded: 'array_filter'\r\n$z83=idal(array(160,179,179,160,184,158,167,168,173,181,164,179));\r\n\r\n\/\/ Decoded: 'eval(gzinflate('\r\n$tf082308=idal(array(164,183,160,173,233,166,187,168,175,167,173,160,181,164,233));\r\n\r\n\/\/ Decoded: '));'\r\n$h2bbd60=idal(array(232,232,250));\r\n\r\n\/\/ Decoded: 'eval'\r\n$z61=idal(array(164,183,160,173));\r\n\r\n\/\/ Decoded: 'substr'\r\n$nfa8b0b=idal(array(178,180,163,178,181,179));\r\n\r\n\/\/ Decoded: '$'\r\n$x1418=idal(array(229));\r\n\r\n\/\/ Decoded: 'preg_replace'\r\n$e7r27=idal(array(177,179,164,166,158,179,164,177,173,160,162,164));\r\n\r\n\/\/ Decoded: get_defined_functions\r\n$kx3=idal(array(166,164,181,158,165,164,167,168,175,164,165,158,167,180,175,162,181,168,174,175,178));\r\n\r\n\/\/ $l84 holds an array of all of the functions within the script. \r\n$l84=$kx3();\r\n\r\n\/\/ Decoded: 'pack'\r\n$d7f=idal(array(177,160,162,170));\r\n\r\n\/\/ Decoded: 'gzinflate'\r\n$tc2e980=idal(array(166,187,168,175,167,173,160,181,164));\r\n\r\n$mzoha=null;\r\n\r\n\/\/ Decoded: 'user'\r\n\/\/ Decoded: 'p96167b'\r\n\/\/ This line performs an array_filter against all of the user functions within\r\n\/\/ the script. It passes these function names to the the p96167b() function.\r\n$l84=$z83($l84[idal(array(180,178,164,179))], idal(array(177,248,247,240,247,246,163)));\r\n\r\n\/\/ At this point, $l84 contains all of the function names in the script that \r\n\/\/ start with a string of 'b4ebd'. The remaining data from these function names\r\n\/\/ is appended to an array.\r\n$p6a = array();\r\nforeach($l84 as $f){\r\n\t$p6a[] = $nfa8b0b($f,5);\r\n};\r\n\r\n\/\/ Decoded: 'H*'\r\n\/\/ This line unhexes the data contained within the $p6a variable. \r\n$ke03 = $d7f(idal(array(137,235)), $l8d4345($mzoha,$p6a));\r\n\r\n\/\/ Decoded: ke03\r\n$r80 = idal(array(170,164,241,242));\r\n\r\n\/\/ This line does a number of things. It translates to the following:\r\n\/\/ preg_replace('\/.*\/e', eval(gzinflate($ke03));, $mzoha );\r\n\/\/ Simply put, it will gzinflate and evaluate the data. \r\n$e7r27($b99, $tf082308.$x1418.$r80.$h2bbd60, $mzoha);\r\n\r\n\/\/ This function looks at the provided string and performs a 'substr' against\r\n\/\/ the first 5 bytes. It proceeds to compare it against a string of 'b4ebd' \r\n\/\/ to see if it's a match.\r\nfunction p96167b($zwaxa){\r\n\tglobal $nfa8b0b; \r\n\t\/\/ Decoded: 'b4ebd'\r\n\treturn ($nfa8b0b($zwaxa,0,5) == idal(array(163,245,164,163,165)));\r\n}\r\n\r\n\/\/ This function is responsible for XORing the provided array of \r\n\/\/ bytes against 193.\r\nfunction idal($zwaxa){\r\n\tglobal $mzoha; \r\n\t$qoazis34 = $mzoha;\r\n\tfor($i=0; $i &lt; count($zwaxa); $i++)\r\n\t\t$qoazis34.=chr($zwaxa[$i]) ^ chr(193);\r\n\treturn $qoazis34;\r\n}\r\n\r\n?&gt;\r\n<\/pre>\n<p>Using this information, we can decode the provided PHP using the following Python code:<\/p>\n<pre class=\"lang:default decode:true \">import sys, re, binascii, zlib, base64\r\n\r\ndef gzinflate(compressed_data):\r\n\treturn zlib.decompress(compressed_data, -15)\r\n\r\nphp_file = \"MerlinsMaze.php\"\r\nphp_fh = open(php_file, 'rb')\r\nphp_data = php_fh.read()\r\nphp_fh.close()\r\n\r\nall_data = \"\"\r\nfound_function_names = re.findall(\"function b4ebd(\\w+)\", php_data)\r\nall_data = \"\".join(found_function_names)\r\n\r\nprint gzinflate(binascii.unhexlify(all_data))<\/pre>\n<p>This leaves us with the following decoded PHP code:<\/p>\n<pre class=\"lang:default decode:true \">$alalalala = \".backup.bin\";\r\n$oilk = \"5c59295d2d2461565\";\r\nif(!(file_exists($alalalala))) {\r\n\t$jqqqqq = fopen($alalalala, 'w+');\r\n\tfwrite($jqqqqq, \"\");\r\n\tfclose($jqqqqq);\r\n}\r\n$ukl = \"\";\r\nif(filesize($alalalala) &gt; 0)\r\n{\r\n\t$jqqqqq = fopen($alalalala, 'r+');\r\n\t$khfmal = fread($jqqqqq, filesize($alalalala));\r\n\t$ghhhggv = split(\", \", $khfmal);\r\n\tfclose($jqqqqq);\r\n}\r\nelse{ $ghhhggv = array();}\r\n$oilk = $oilk+\"529206651412d2a5d\";\r\n$ukl.=\"16727839782a2524105a4\";\r\nif(isset($_GET['path'])){\r\n\t$p = $_GET['path'];\r\n\t$to_write = \"\";\r\n\t$ghhhggv[] = $p;\r\n\t$jqqqqq = fopen($alalalala, 'w');\r\n\tfwrite($jqqqqq, implode(\", \", $ghhhggv));\r\n\tfclose($jqqqqq);\r\n}\r\n$ukl.=\"51550125a203\";\r\nfunction l(){\r\n\tglobal $alalalala;\r\n\t$jqqqqq = fopen($alalalala, 'w+');\r\n\tfwrite($jqqqqq, \"\");\r\n\tfclose($jqqqqq);\r\n\t$c = rand(0, 10);\r\n\tif($c&lt;1){\r\n\t\treturn \"A super fluffly dog mauls you to death.\";\r\n\t}elseif($c&lt;2){\r\n\t\treturn \"You have been defeated by David Bowie's juggling.\";\r\n\t}elseif($c&lt;3){\r\n\t\treturn \"The maze drives you to madness and you spiral into a world of chaos.\";\r\n\t}elseif($c&lt;4){\r\n\t\treturn \"Jennifer Connelly's teenage angst defeats you.\";\r\n\t}elseif($c&lt;5){\r\n\t\treturn \"You die of dysentery.\";\r\n\t}elseif($c&lt;6){\r\n\t\treturn \"You don't really die. You just kind of lie there and give up.\";\r\n\t}elseif($c&lt;7){\r\n\t\treturn \"A white owl flies overhead. Oh, also, you lose.\";\r\n\t}elseif($c&lt;8){\r\n\t\treturn \"A weird goblin shows up and scares you away.\";\r\n\t}else{\r\n\t\treturn \"Jim Henson returns from the dead and possesses you.\";\r\n\t}\r\n}\r\nfunction c2($okal){\r\n\t$f=0;\r\n\tif($okal[20]!='f'){$f=4;}\r\n\tif(strtolower(substr(\"F12AB867ACDBE57\",8,2))!=chr(97).$okal[22]){$f=1;}\r\n\tif($okal[25]!='p'){$f=3;}\r\n\tif($okal[22]==$okal[28]){$f=8;}\r\n\treturn $f;\r\n}\r\n$ukl.=\"b20665a5\";\r\n$ukl.=\"56220565a455\";\r\n$k = array();\r\nforeach ($ghhhggv as $a){\r\n\tswitch(trim($a)){\r\n\t\tcase(\"Up\"):\r\n\t\t\t$k[] = \"A\";\r\n\t\t\tbreak;\r\n\t\tcase(\"Down\"):\r\n\t\t\t$k[] = \"0\";\r\n\t\t\tbreak;\r\n\t\tcase(\"Back\"):\r\n\t\t\t$k[] = \"B\";\r\n\t\t\tbreak;\r\n\t\tcase(\"Forward\"):\r\n\t\t\t$k[] = \"8\";\r\n\t\t\tbreak;\r\n\t\tcase(\"Left\"):\r\n\t\t\t$k[] = \"7\";\r\n\t\t\tbreak;\r\n\t\tcase(\"Right\"):\r\n\t\t\t$k[] = \"F\";\r\n\t\t\tbreak;\r\n\t\tcase(\"246 Degrees\"):\r\n\t\t\t$k[] = \"3\";\r\n\t\t\tbreak;\r\n\t\tcase(\"94 Degrees\"):\r\n\t\t\t$k[] = \"C\";\r\n\t\t\tbreak;\r\n\t\tcase(\"Skip\"):\r\n\t\t\t$k[] = \"E\";\r\n\t\t\tbreak;\r\n\t\tcase(\"Stroll\"):\r\n\t\t\t$k[] = \"1\";\r\n\t\t\tbreak;\r\n\t\tcase(\"Jump\"):\r\n\t\t\t$k[] = \"D\";\r\n\t\t\tbreak;\r\n\t\tcase(\"Run\"):\r\n\t\t\t$k[] = \"9\";\r\n\t\t\tbreak;\r\n\t\tcase(\"North\"):\r\n\t\t\t$k[] = \"2\";\r\n\t\t\tbreak;\r\n\t\tcase(\"South\"):\r\n\t\t\t$k[] = \"5\";\r\n\t\t\tbreak;\r\n\t\tcase(\"East\"):\r\n\t\t\t$k[] = \"6\";\r\n\t\t\tbreak;\r\n\t\tcase(\"West\"):\r\n\t\t\t$k[] = \"4\";\r\n\t\t\tbreak;\r\n\t\tdefault:\r\n\t\t\tprint \"DANGER \".$a;\r\n\t}\r\n}\r\n$ukl.=\"55f5023352d295d456c14\";\r\nfunction x_oiflksf($goijas, $oijasf) {\r\n\t$goijas = implode(\"\", $goijas);\r\n\t$o_joijasd = '';\r\n\tfor($i=0; $i&lt;strlen($oijasf);)\r\n\t{\r\n\t\tfor($j=0;($j&lt;strlen($goijas) &amp;&amp; $i&lt;strlen($oijasf));$j++,$i++)\r\n\t\t{\r\n\t\t \t$o_joijasd .= $oijasf{$i} ^ $goijas{$j};\r\n\t\t}\r\n\t}  \r\n return $o_joijasd;\r\n}\r\n$ukl.=\"022f325f1f16\";\r\nfunction lkj080jo($oilk, $ukl){\r\n\treturn x_oiflksf($oilk, $ukl);\r\n}\r\n$ukl.=\"4544424724353666\";\r\n$ukl.=\"544127\";\r\nfunction bxmsadfj($a, $b){\r\n\t$a.=\"2b37174a\";\r\n\t$u = pack('H*', $a);\r\n\treturn x_oiflksf($b, $u);\r\n}\r\nfunction get_key(){return gnfjk();}\r\n$ukl.=\"634a58585c425a2f\";\r\nfunction uan($kfj){\r\n\t$y = sizeof($kfj);\r\n\tif($y &gt; 20){\r\n\t\t$lxkf = implode(\"\",$kfj);\r\n\t\tif(strrev(substr($lxkf,10,4))!=\"2156\"){return 0;}\r\n\t\tif($lxkf[5]!=$lxkf[6]){return 0;}\r\n\t\tpreg_match_all('\/.{1}(.+)\/', $lxkf, $o);\r\n\t\tif($o[1][0][1]!=\"6\"){return 0;}\r\n\t\tif(ord($kfj[0])+ord($kfj[18])!=140){return 0;}\r\n\t}\r\n\tif((100\/5\/2) == 10){return 1;}\r\n\tif($kfj[3]=='1'){return 0;}\r\n\treturn 0;\r\n}\r\n$ukl.=\"2437665a58345b2f3524541d1\";\r\nfunction gnfjk(){return l();}\r\n$ukl.=\"67154535b61362c325d13\";\r\nfunction lkj($l){\r\n\t$zl = sizeof($l);\r\n\tif($zl&gt;12){\r\n\t\tif($l[3]!=substr(\"ABCDEFABCDEF\",-5,1)){return true;}\r\n\t\tif($l[20*10\/20-2]!=substr(\"759480376789\",5,1)){return true;}\r\n\t\tif(substr(base64_encode(\"orb\"),1,1)!=$l[9]){return true;}\r\n\t}\r\n\tif($zl&gt;22){\r\n\t\t$qzlf = implode(\"\",$l);\r\n\t\tpreg_match_all('\/.{19}(.).(.)\/', $qzlf, $yy);\r\n\t\tif($yy[1][0]!=\"5\"){return true;}\r\n\t\tif($yy[2][0]!=\"B\"){return true;}\r\n\t}\r\n\treturn false;\r\n}\r\nfunction hj($h){\r\n\t$z = sizeof($h);\r\n\t$f = 1;\r\n\tif($z &gt; 28){\r\n\t\tif(chr(11*6)!=$h[21]){$f=0;}\r\n\t\tif(chr(49+1+1-(8*0))!=$h[1]){$f=0;}\r\n\t\tif(chr(ord($h[19])+1)!=$h[27]){$f=0;}\r\n\t\tif(substr(strrev(base64_encode(\"!1!Goblin King oh my!1!\")),25,1)!=$h[26]){$f=0;}\r\n\t}\r\n\tif($z&gt;25){\r\n\t\tif((ord(\"a\")-40)!=ord($h[23])){$f=0;}\r\n\t\tif(substr(\"53812E7F82ABFE\",5,2)!=($h[17].$h[24])){$f=0;}\r\n\t}\r\n\treturn $f;\r\n}\r\nfunction c($k){\r\n\tglobal $ukl;\r\n\t$sz = sizeof($k);\r\n\tif($sz == 1){\r\n\t\tif($k[0] == \"F\"){   \r\n\t\t\t$k1 = \"2365\";\r\n\t\t\t$x = \"AF05236525\";\r\n\t\t\t$k1 = $k1+$x;\r\n\t\t} else { return l(); }\r\n\t}\r\n\tif($sz &gt; 6){         \r\n\t\t$j = ord($k[5]);\r\n\t\tif($j != 67){ return l();}\r\n\t}\r\n\tif($sz &gt; 5){\r\n\t\tif($k[4] != \"4\"){return get_key();}\r\n\t}\r\n\tif(uan($k)!=1){return gnfjk();}\r\n\tif(hj($k)!=1){return l();}\r\n\tif($sz&gt;24){\r\n\t\tif('A'!=$k[16]){return get_key();}\r\n\t}\r\n\tif($sz &gt; 8){\r\n\t\t$o = ord($k[7]);  \r\n\t\t$o = ($o + 17) \/ 2;\r\n\t\t$o = $o * 4;\r\n\t\t$o = chr($o - 56);\r\n\t\tif($o != 'l'){return l();}else{$key2 = \"EB45C\";}\r\n\t}\r\n\tif(lkj($k)){return gnfjk();}\r\n\tif(($sz+31) == 63){\r\n\t\tif(array_slice($k,29,3)!=array(\"B\",\"A\",\"D\")){return get_key();}\r\n\t\t$jl = bxmsadfj($ukl, $k);\r\n\t\t$d = c2($jl);\r\n\t\tif($d &gt; 5){ \r\n\t\t\t$t1 = \"You have naviga\";\r\n\t\t\t$t2 = \"Surprise! You've encountered \";\r\n\t\t\t$t1 .= \"ted the ma\";\r\n\t\t\t$tl = \"The Goblin King. \";\r\n\t\t\t$t2 .= \"the Gob\";\r\n\t\t\t\/* Hi. Congrats on getting this far. *\/\r\n\t\t\t$t1 .= \"ze and de\";\r\n\t\t\t$t2 .= \"lin King!\";\r\n\t\t\t\/*\r\n\t\t\t\tOnce upon a time, there was a beautiful young \r\n\t\t\t\tgirl whose stepmother \r\n\t\t\t\talways made her stay home with the baby. And the baby was a spoiled \r\n\t\t\t\tchild, and wanted everything to himself, and \r\n\t\t\t\tthe young girl was \r\n\t\t\t\tpractically a slave. But what no one knew is that the king of the \r\n\t\t\t\tgoblins had fallen in love with the the \r\n\t\t\t\tgirl, and he had given her \r\n\t\t\t\tcertain powers. So one night, when the baby had be \r\n\t\t\t\tparticularly \r\n\t\t\t\tcruel \r\n\t\t\t\tto \r\n\t\t\t\ther, she \r\n\t\t\t\tcalled \r\n\t\t\t\ton the goblins \r\n\t\t\t\tfor \r\n\t\t\t\thelp!\r\n\t\t\t*\/\r\n\t\t\t$t1 .= \"feated Dav\";\r\n\t\t\t$t1 .= \"id Bowie! &lt;p&gt;K\";\r\n\t\t\t$tl .= \"He shall return with this key: \";\r\n\t\t\t$t2 .= \"You found the key: \";\r\n\t\t\t$t1 .= \"ey: \".$jl.\"&lt;\/p&gt;\";\r\n\t\t\tif(substr($t1,23,1)!=$jl[14]){return str_replace('.','!',str_replace(\" \",\"_\",$jl));}\r\n\t\t\tif($k[9]!=$k[1]){return $t2;}\r\n\t\t\tif($k[31]==$k[0]){return $tl;}\r\n\t\t\tif($k[15]==$k[16] and lcfirst($k[15])==$jl[15]){return $t1;}\r\n\t\t\treturn l();\r\n\t\t}else{return l();}\r\n\t}elseif($sz&gt;32){return l();}\r\n\treturn \"\";\r\n}\r\n$khfmal = implode(\", \", $ghhhggv);\r\n$t = c($k);\r\n<\/pre>\n<p>At this point we\u2019re able to trace what is happening when we enter various options within the script. Data is written to a \u2018.backup.bin\u2019 file to keep track of what data was previously inputted. After stepping through the code and seeing what checks have been included, we determine that the following order must be provided:<\/p>\n<ol>\n<li>Right<\/li>\n<li>246 Degrees<\/li>\n<li>East<\/li>\n<li>Back<\/li>\n<li>West<\/li>\n<li>94 Degrees<\/li>\n<li>94 Degrees<\/li>\n<li>Up<\/li>\n<li>Down<\/li>\n<li>246 Degrees<\/li>\n<li>East<\/li>\n<li>South<\/li>\n<li>Stroll<\/li>\n<li>North<\/li>\n<li>Left<\/li>\n<li>Up<\/li>\n<li>Up<\/li>\n<li>Skip<\/li>\n<li>Right<\/li>\n<li>South<\/li>\n<li>246 Degrees<\/li>\n<li>Back<\/li>\n<li>94 Degrees<\/li>\n<li>Run<\/li>\n<li>Left<\/li>\n<li>South<\/li>\n<li>Run<\/li>\n<li>East<\/li>\n<li>246 Degrees<\/li>\n<li>Back<\/li>\n<li>Up<\/li>\n<li>Jump<\/li>\n<\/ol>\n<p>Entering this into the program leaves us with the following:<\/p>\n<p><a href=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-6.png\"><div style=\"max-width:100%\" data-width=\"500\"><span class=\"ar-custom\" style=\"padding-bottom:192.8%;\"><img loading=\"lazy\" decoding=\"async\"  class=\"aligncenter size-large wp-image-20721 lozad\"  data-src=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-6.png\" alt=\"labyrenth_random-6\" width=\"500\" height=\"964\" \/><\/span><\/div><\/a><\/p>\n<p>This leaves us with a key of PAN{Life is a maze of complications. Also, puppets are sometimes involved. Deal with it.}<\/p>\n<h3><strong>Random 5 Challenge: You might have to be a snake charmer to crack the newest version of APT Maker Pro. What's the worst that could be in ten lines of Python?<\/strong><\/h3>\n<p><em>Challenge Created By: Gabriel Kirkpatrick <\/em><a href=\"https:\/\/twitter.com\/gabe_k\" rel=\"nofollow,noopener\" ><em>@gabe_k<\/em><\/a><\/p>\n<p><a href=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-7.png\"><div style=\"max-width:100%\" data-width=\"500\"><span class=\"ar-custom\" style=\"padding-bottom:57.2%;\"><img loading=\"lazy\" decoding=\"async\"  class=\"aligncenter size-large wp-image-20724 lozad\"  data-src=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-7.png\" alt=\"labyrenth_random-7\" width=\"500\" height=\"286\" \/><\/span><\/div><\/a><\/p>\n<p>TLOP is the final challenge of the LabyREnth CTF random track. When you download it, you're given a file called TLOP.pyw. If you run it, it will open a program called APT Maker Pro - UNREGISTERED TRIAL VERSION. There's a button labeled \"Generate APT\" which informs you that you need to activate APT Maker Pro, and a button labeled \"Activate APT Maker Pro!\" which brings up a dialog asking for the product key. The challenge here is to find the product key to activate the program, allowing you to generate the APT.<\/p>\n<p>Let's start reversing! TLOP.pyw is a pyw file, not a py file, which is pretty much the same except that on Windows pyw, files don't bring up a command line window.\u00a0 It can be renamed to py if you'd like to print debug info to STDOUT. Once you open up the file, you'll see ten lines of python (get it? Ten Lines of Python? TLOP... it's stupid). Most of the code after the imports simply sets up a TKinter window, which is actually the splash screen. The last line is where it gets interesting:<\/p>\n<p>exec marshal.loads(zlib.decompress(&lt;longgggggggg gibberish string&gt;))<\/p>\n<p>So it's an exec statement being passed the result of marshal.loads, which is loading some zlib'd data. Marshal is the Python module for serializing and deserializing builtin Python types. If we remove the exec and run the same line in the Python shell, we can see the result is a code object. Exec statements in Python accept two types of input, strings of Python code and code objects which contain compiled Python bytecode. Code objects are most often seen used in .pyc files, which are compiled Python files.\u00a0 \u00a0These compiled Python files are generated when Python modules are imported. Pyc files contain a 32-bit magic number specifying the Python version, a 32-bit timestamp of the compilation, and a marshaled code object. Since we already have a marshaled code object, we can turn this into a .pyc file with the following code:<\/p>\n<pre class=\"lang:default decode:true \">import py_compile\r\nimport zlib\r\no = open('stage1.pyc', 'wb')\r\no.write(py_compile.MAGIC)\r\no.write('\\x00' * 4) # null timestamp\r\no.write(zlib.decompress(&lt;longgggggggg gibberish string&gt;))\r\no.close()\r\n<\/pre>\n<p>Using uncompyle2 we can decompile the stage1.pyc file we created.<\/p>\n<p><a href=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-8.png\"><div style=\"max-width:100%\" data-width=\"500\"><span class=\"ar-custom\" style=\"padding-bottom:79.4%;\"><img loading=\"lazy\" decoding=\"async\"  class=\"aligncenter size-large wp-image-20727 lozad\"  data-src=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-8.png\" alt=\"labyrenth_random-8\" width=\"500\" height=\"397\" \/><\/span><\/div><\/a><\/p>\n<p style=\"text-align: center;\"><em>Decompiled output<\/em><\/p>\n<p>The resulting decompilation contains a class called AptMaker which contains most of the code for the UI. After the class there is a RC4 function, as well as another exec on the result of another marshal. We can build the exec\u2019d object into another pyc file to analyze it.<\/p>\n<pre class=\"lang:default decode:true \">import py_compile\r\nimport zlib\r\nimport base64\r\no = open('stage2.pyc', 'wb')\r\no.write(py_compile.MAGIC)\r\no.write('\\x00' * 4) # null timestamp\r\no.write(zlib.decompress(base64.b64decode(&lt;longgggggggg gibberish string&gt;)))\r\no.close()\r\n<\/pre>\n<p>If we attempt to decompile the stage2.pyc with uncompyle2, we get the following error:<\/p>\n<pre class=\"lang:default decode:true \">$ uncompyle2 stage2.pyc\r\n# 2016.10.02 23:03:37 PDT\r\n#Embedded file name: a\r\n### Can't uncompyle stage2.pyc\r\nTraceback (most recent call last):\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/__init__.py\", line 197, in main\r\n    uncompyle_file(infile, outstream, showasm, showast, deob)\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/__init__.py\", line 130, in uncompyle_file\r\n    uncompyle(version, co, outstream, showasm, showast, deob)\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/__init__.py\", line 117, in uncompyle\r\n    walker.gen_source(ast, customize)\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/Walker.py\", line 1406, in gen_source\r\n    self.print_(self.traverse(ast, isLambda=isLambda))\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/Walker.py\", line 492, in traverse\r\n    self.preorder(node)\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/spark.py\", line 692, in preorder\r\n    self.preorder(kid)\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/spark.py\", line 692, in preorder\r\n    self.preorder(kid)\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/spark.py\", line 692, in preorder\r\n    self.preorder(kid)\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/spark.py\", line 687, in preorder\r\n    self.default(node)\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/Walker.py\", line 1180, in default\r\n    self.engine(table[key], node)\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/Walker.py\", line 1130, in engine\r\n    self.preorder(node[entry[arg]])\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/spark.py\", line 685, in preorder\r\n    func(node)\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/Walker.py\", line 878, in n_mkfunc\r\n    self.make_function(node, isLambda=0)\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/Walker.py\", line 1330, in make_function\r\n    self.print_docstring(indent, code.co_consts[0])\r\n  File \"\/usr\/local\/lib\/python2.7\/site-packages\/uncompyle2\/Walker.py\", line 544, in print_docstring\r\n    docstring = repr(docstring.expandtabs())[1:-1]\r\nAttributeError: 'code' object has no attribute 'expandtabs'\r\n# decompiled 0 files: 0 okay, 1 failed, 0 verify failed\r\n# 2016.10.02 23:03:37 PDT\r\n\r\n<\/pre>\n<p>Throwing it at other Python decompilers will likely yield similar errors, so we\u2019re going to have to take a different approach. Since it won\u2019t decompile, we can attempt to disassemble the bytecode. Python has a built-in module for disassembling Python bytecode called dis, which we can use to disassemble the pyc file we produced by running the following code:<\/p>\n<pre class=\"lang:default decode:true \">import marshal\r\nimport dis\r\no = open('stage2.pyc', 'rb')\r\no.read(8)\r\nc = marshal.load(o)\r\no.close()\r\ndis.dis(c)\r\n<\/pre>\n<p>The following output is produced from the code above:<\/p>\n<pre class=\"lang:default decode:true \">0 LOAD_CONST               0 (&lt;code object verify_license at 0x610458, file \"\", line -1&gt;)\r\n  3 MAKE_FUNCTION            0\r\n  6 STORE_NAME               0 (verify_license)\r\n  9 LOAD_CONST               1 (None)\r\n 12 RETURN_VALUE\r\n<\/pre>\n<p>The code output is relatively simple. The first instruction, LOAD_CONST 0, pushes the constant at index 0 in the current code object onto the stack. After that we have MAKE_FUNCTION 0, which pops a code object off of the stack and turns it into a function object. The third instruction, STORE_NAME 0, pops the top item on the stack (the function we just created) and stores it with the name at index 0 in the code object. \u00a0That name in this case is \u201cverify_license\u201d. What this sequence of instructions does in practice is to create a function named verify_license. We can see the verify_license function is referenced in our previous decompilation inside the \u201cis_licensed\u201d function:<\/p>\n<pre class=\"lang:default decode:true \">    def is_licensed(self):\r\n        return verify_license(self.license_key.zfill(25))\r\n<\/pre>\n<p>The two last instructions simply push the constant None to the stack, and then return it. This is present in all Python code objects that don\u2019t have an explicit return value because all Python code objects must return something.<\/p>\n<p>Now that we know that all this code is doing is creating a function, we can actually run it and use the verify_license function from the Python shell by importing the stage2.pyc file. If we run the following code we can start to play around with the function from the shell:<\/p>\n<pre class=\"lang:default decode:true \">&gt;&gt;&gt; from stage2 import *\r\n&gt;&gt;&gt; verify_license('A' * 100)\r\nFalse\r\n<\/pre>\n<p>From this we know that verify_license is a function that returns a boolean. If we disassemble the function we will see the following:<\/p>\n<pre class=\"lang:default decode:true \">  0 LOAD_CONST               0 (&lt;code object check_login at 0x2d46e0, file \"shell.py\", line -1&gt;)\r\n  3 LOAD_CONST               1 (None)\r\n  6 DUP_TOP\r\n  7 EXEC_STMT\r\n  8 LOAD_NAME                0 ( )\r\n 11 RETURN_VALUE\r\n<\/pre>\n<p>Let\u2019s take a look at this instruction by instruction:<\/p>\n<pre class=\"lang:default decode:true \">  0 LOAD_CONST               0 (&lt;code object check_login at 0x2d46e0, file \"shell.py\", line -1&gt;)<\/pre>\n<p>First off it\u2019s pushing constant 0, which is a code object, to the stack.<\/p>\n<pre class=\"&quot;lang:default\" decode:true=\"\">  3 LOAD_CONST               1 (None)\r\n<\/pre>\n<p>The next thing it does is push constant 1, which is None, to the stack.<\/p>\n<pre class=\"&quot;lang:default\" decode:true=\"\">  6 DUP_TOP\r\n<\/pre>\n<p>DUP_TOP duplicates the top item of the stack, so it pushes another None to the stack.<\/p>\n<pre class=\"&quot;lang:default\" decode:true=\"\">  7 EXEC_STMT\r\n<\/pre>\n<p>EXEC_STMT is the equivalent of the \u201cexec\u201d keyword in regular Python. It takes three parameters, a code object or Python string, and two optional parameters containing global and local variables. In this case the code object is the one pushed at the start of the function, and the global and locals are not used, so those are the two Nones on the stack.<\/p>\n<pre class=\"lang:default decode:true \">  8 LOAD_NAME                0 ( )\r\n 11 RETURN_VALUE\r\n<\/pre>\n<p>This code pushes the value stored for name 0 and returns it. Name 0 here appears to be \u201c \u201c, which is not a valid Python name and is not referenced anywhere else in the function, so it\u2019s safe to assume this gets set by the code run by the EXEC_STMT.<\/p>\n<p>Since there\u2019s not much code in here, we can assume the meat of the code is inside the code object that gets exec\u2019d. We can disassemble that code object by running the following:<\/p>\n<pre class=\"lang:default decode:true \">&gt;&gt;&gt; dis.dis(verify_license.func_code.co_consts[0])\r\n<\/pre>\n<p>Unlike the previous times we\u2019ve run dis in here, you\u2019ll start seeing incredibly long output that looks something like this:<\/p>\n<p><a href=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-9.png\"><div style=\"max-width:100%\" data-width=\"500\"><span class=\"ar-custom\" style=\"padding-bottom:107%;\"><img loading=\"lazy\" decoding=\"async\"  class=\"aligncenter size-large wp-image-20730 lozad\"  data-src=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-9.png\" alt=\"labyrenth_random-9\" width=\"500\" height=\"535\" \/><\/span><\/div><\/a><\/p>\n<p style=\"text-align: center;\"><em>A bird\u2019s eye view of the disassembly<\/em><\/p>\n<p>If we actually let the disassembly run until it\u2019s entirely finished we won\u2019t actually get any useful information. The entire output is thousands of EXTENDED_ARG instructions with increasingly large arguments, followed by a JUMP_FORWARD to the same large value.<\/p>\n<p>So what\u2019s the issue? The Python runtime stores the arguments for Python instructions in a signed 32-bit integer called oparg. Python instructions that have arguments are 3 bytes long, 1 byte for the opcode and 2 bytes for the oparg value. The problem with this is that instructions can only set the lower 16-bits of the oparg, instead of the whole 32-bits. To get around this limitation, Python has an instruction called EXTENDED_ARG, which shifts its argument to the left 16-bits, allowing you to set the upper 16-bit in one instruction, and the lower 16 in the next. If you put multiple EXTENDED_ARG instructions in a row, the Python runtime will simply keep shifting the 32-bit integer that is oparg, and bits will fall off the end. However, if you disassemble that code with dis, oparg is stored in a Python number. Since dis uses a Python number instead of a fixed 32-bit integer, the number keeps on growing with every single EXTENDED_ARG instruction.<\/p>\n<p>After coming across this and a few other issues with how dis handles funky bytecode, I wrote my own assembler\/disassembler called <a href=\"https:\/\/github.com\/gabe-k\/pyasm\" rel=\"nofollow,noopener\" >pyasm<\/a>, which we can use to produce a slightly more useful disassembly.<\/p>\n<p>If we run dispy.py on stage2.pyc, we will get stage2.pyasm. The code object we are looking at starts at line 11 in stage2.pyasm, with the actual instructions starting at line 100. By looking at the first bunch of instructions we start to notice a pattern:<\/p>\n<pre class=\"lang:default decode:true \">\t\t\t\t\t\tEXTENDED_ARG 101\r\n\t\t\t\t\t\tEXTENDED_ARG 28169\r\n\t\t\t\t\t\tEXTENDED_ARG 2305\r\n\t\t\t\t\t\t133 * EXTENDED_ARG 0xffff\r\n\t\t\t\t\t\tEXTENDED_ARG 356\r\n\t\t\t\t\t\tEXTENDED_ARG 28169\r\n\t\t\t\t\t\tEXTENDED_ARG 2305\r\n\t\t\t\t\t\t133 * EXTENDED_ARG 0xffff\r\n\t\t\t\t\t\tEXTENDED_ARG 28185\r\n\t\t\t\t\t\tEXTENDED_ARG 2305\r\n\t\t\t\t\t\t133 * EXTENDED_ARG 0xffff\r\n\t\t\t\t\t\tEXTENDED_ARG 602\r\n\t\t\t\t\t\tEXTENDED_ARG 28169\r\n\t\t\t\t\t\tEXTENDED_ARG 2305\r\n\t\t\t\t\t\t133 * EXTENDED_ARG 0xffff\r\n\t\t\t\t\t\tEXTENDED_ARG 612\r\n\t\t\t\t\t\tEXTENDED_ARG 28169\r\n\t\t\t\t\t\tEXTENDED_ARG 2305\r\n\t\t\t\t\t\t133 * EXTENDED_ARG 0xffff\r\n\t\t\t\t\t\tEXTENDED_ARG 2660\r\n\t\t\t\t\t\tEXTENDED_ARG 28169\r\n\t\t\t\t\t\tEXTENDED_ARG 2305\r\n\t\t\t\t\t\t133 * EXTENDED_ARG 0xffff\r\n<\/pre>\n<p>We can see there are consistently two-three EXTENDED_ARG instructions with different arg values, followed by 133 using the arg value 0xFFFF. If we scroll down to the very end of the instructions at line 1390 we can see the last two instructions deviate slightly from this pattern:<\/p>\n<pre class=\"lang:default decode:true \">                                                EXTENDED_ARG 65533\r\n\t\t\t\t\t\tJUMP_FORWARD 52549\r\n<\/pre>\n<p>We can work out the actual argument for JUMP_FORWARD as 65533 &lt;&lt; 16 | 52549 which comes out to 0xfffdcd45. Oparg is signed, so it is actually -144059, which is actually a jump back to the second byte of the bytecode, instead of the first byte where it normally starts execution. This is a form of instruction overlapping, since the second byte is the argument for the first instruction, the code is hidden in the arguments of all of the EXTENDED_ARG instructions.<\/p>\n<p>If we open the stage2.pyc file in a hex editor, we can delete the first byte of the bytecode, so that it starts disassembly from the second byte. To do this we just delete the byte at 0x6D in stage2.pyc, then change the 32-bit int containing the length of the bytecode 0x69 from 0x232BC to 0x232BB. Now if we disassemble the file we should see the following at the start of the code:<\/p>\n<pre class=\"lang:default decode:true \">                                        LOAD_NAME 0x9100 # license_key\r\n\t\t\t\t\tNOP \r\n\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\tNOP \r\n<\/pre>\n<p>Now it\u2019s starting to look more like normal bytecode. The first thing it does is load name 0x9100 which is \u201clicense key\u201d and then jump forward 401 bytes. If we cut out the 401 bytes after the jump forward and disassemble again we get even more:<\/p>\n<pre class=\"lang:default decode:true \">                                                LOAD_NAME 0x9100 # license_key\r\n\t\t\t\t\t\tNOP \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tLOAD_CONST 37121 # 0\r\n\t\t\t\t\t\tNOP \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tNOP \r\n<\/pre>\n<p>Cool, we\u2019re starting to get more. Now if we do this a few more times, we start to see some interesting stuff:<\/p>\n<pre class=\"lang:default decode:true \">\t\t\t\t\t\tLOAD_NAME 0x9100 # license_key\r\n\t\t\t\t\t\tNOP \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tLOAD_CONST 37121 # 0\r\n\t\t\t\t\t\tNOP \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tBINARY_SUBSCR \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tSTORE_NAME 37122 #   \r\n\t\t\t\t\t\tNOP \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tLOAD_CONST 37122\r\n\t\t\t\t\t\tNOP \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tLOAD_CONST 37130 # 542\r\n\t\t\t\t\t\tNOP \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tBINARY_SUBSCR \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tSTORE_NAME 37123 #    \r\n\t\t\t\t\t\tNOP \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tLOAD_CONST 37131 # &lt;code object &lt;module&gt; at 0x710800, file \"cmp_eq.py\", line -1&gt;\r\n\t\t\t\t\t\tNOP \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tLOAD_CONST 0x9100 # None\r\n\t\t\t\t\t\tNOP \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tNOP \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tEXEC_STMT \r\n\t\t\t\t\t\tJUMP_FORWARD 401\r\n\t\t\t\t\t\tNOP \r\n<\/pre>\n<p>If we strip out all the JUMP_FORWARD and NOP instructions it becomes easier to see what it\u2019s doing:<\/p>\n<pre class=\"lang:default decode:true \">LOAD_NAME 0x9100 # license_key\r\n\t\t\t\t\t\tLOAD_CONST 37121 # 0\r\n\t\t\t\t\t\tBINARY_SUBSCR \r\n\t\t\t\t\t\tSTORE_NAME 37122 #   \r\n\t\t\t\t\t\tLOAD_CONST 37122\r\n\t\t\t\t\t\tLOAD_CONST 37130 # 542\r\n\t\t\t\t\t\tBINARY_SUBSCR \r\n\t\t\t\t\t\tSTORE_NAME 37123 #    \r\n\t\t\t\t\t\tLOAD_CONST 37131 # &lt;code object &lt;module&gt; at 0x710800, file \"cmp_eq.py\", line -1&gt;\r\n\t\t\t\t\t\tLOAD_CONST 0x9100 # None\r\n\t\t\t\t\t\tEXEC_STMT \r\n<\/pre>\n<p>It\u2019s taking the variable named \u201clicense_key\u201d, and a constant with the value 0, and doing a BINARY_SUBSCR, which allows you to retrieve a value at an index, and then it is storing it in name 37122, which is a string of whitespace. This is equivalent to the following line of Python:<\/p>\n<p>= license_key[0]<\/p>\n<p>It then does the same thing, but instead of using the variable \u201clicense_key\u201d it uses const 37122, which if we look is actually a PNG, and it gets the value at index 542 and stores it in a different name 37123, which is also whitespace. After that it loads const 37131, which is a code object, and does an exec. If we look at the code in const 37131 it\u2019s fairly simple:<\/p>\n<pre class=\"lang:default decode:true \">                                                                LOAD_NAME 0 #   \r\n\t\t\t\t\t\t\t\tLOAD_NAME 1 #    \r\n\t\t\t\t\t\t\t\tCOMPARE_OP 2\r\n\t\t\t\t\t\t\t\tSTORE_NAME 2 #  \r\n\t\t\t\t\t\t\t\tLOAD_CONST 0 # None\r\n\t\t\t\t\t\t\t\tRETURN_VALUE \r\n<\/pre>\n<p>It is loading the two whitespace named variables that we just set up, comparing them, and storing the result of the comparison in a third whitespace named variable. This is where the license key is actually being checked. We can make the program actually spit out it\u2019s key by simply inserting a print statement in this code object:<\/p>\n<pre class=\"lang:default decode:true \">                                                                LOAD_NAME 0 #   \r\n\t\t\t\t\t\t\t\tLOAD_NAME 1 #\r\n\t\t\t\t\t\t\t\tDUP_TOP\r\n\t\t\t\t\t\t\t\tPRINT_ITEM\r\n\t\t\t\t\t\t\t\tPRINT_NEWLINE    \r\n\t\t\t\t\t\t\t\tCOMPARE_OP 2\r\n\t\t\t\t\t\t\t\tSTORE_NAME 2 #  \r\n\t\t\t\t\t\t\t\tLOAD_CONST 0 # None\r\n\t\t\t\t\t\t\t\tRETURN_VALUE \r\n<\/pre>\n<p>Now if we build the patched stage2.pyasm file with makepy and run verify_license again we can see it print out the correct key:<\/p>\n<blockquote><p>&gt; from stage2solve import *<br \/>\n&gt;&gt;&gt; verify_license('A' * 100)<br \/>\n1<br \/>\n_<br \/>\nW<br \/>\n4<br \/>\nn<br \/>\nn<br \/>\nA<br \/>\n_<br \/>\nb<br \/>\n3<br \/>\n_<br \/>\nT<br \/>\nh<br \/>\n3<br \/>\n_<br \/>\nv<br \/>\nE<br \/>\nR<br \/>\ny<br \/>\n_<br \/>\nb<br \/>\n3<br \/>\nS<br \/>\nT<br \/>\n!<br \/>\nFalse<br \/>\n&gt;&gt;&gt;<\/p>\n<p>So the license key is \u201c1_W4nnA_b3_Th3_vERy_b3ST!\u201d if we go and plug that into the program we can see that it turns green and activates.<\/p>\n<p>&nbsp;<\/p><\/blockquote>\n<p><a href=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-10.png\"><div style=\"max-width:100%\" data-width=\"500\"><span class=\"ar-custom\" style=\"padding-bottom:77.4%;\"><img loading=\"lazy\" decoding=\"async\"  class=\"aligncenter size-large wp-image-20733 lozad\"  data-src=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-10.png\" alt=\"labyrenth_random-10\" width=\"500\" height=\"387\" \/><\/span><\/div><\/a><\/p>\n<p>Yay! Now we can press the \u201cGenerate APT\u201d button, which creates a file called \u201cEVIL_MALWARE_CYBER_PATHOGEN.pyc\u201d. If we run that it will scroll the ASCII art flag across the screen.<\/p>\n<p><a href=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-11.png\"><div style=\"max-width:100%\" data-width=\"500\"><span class=\"ar-custom\" style=\"padding-bottom:12.2%;\"><img loading=\"lazy\" decoding=\"async\"  class=\"aligncenter size-large wp-image-20736 lozad\"  data-src=\"https:\/\/www.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/10\/LabyREnth_Random-11.png\" alt=\"labyrenth_random-11\" width=\"500\" height=\"61\" \/><\/span><\/div><\/a><\/p>\n<p>And there\u2019s the flag! PAN{l1Ke_n0_oN3_ev3r_Wa5}!<\/p>\n<p>That\u2019s it folks! We hope you enjoyed participating in these challenges as much as we enjoyed creating them. Be sure to also check out how other threat researchers solved the challenges from this track:<\/p>\n<p><strong>Random 1:<\/strong><\/p>\n<p><a href=\"https:\/\/0xec.blogspot.de\/2016\/08\/labyrenth-ctf-writeup-random-track.html\" rel=\"nofollow,noopener\" >https:\/\/0xec.blogspot.de\/2016\/08\/labyrenth-ctf-writeup-random-track.html<br \/>\n<\/a><a href=\"https:\/\/github.com\/uafio\/git\/blob\/master\/scripts\/labyREnth-2016\/labyrenth-2016-random-1.txt\" rel=\"nofollow,noopener\" >https:\/\/github.com\/uafio\/git\/blob\/master\/scripts\/labyREnth-2016\/labyrenth-2016-random-1.txt<\/a><\/p>\n<p><strong>Random 2:<\/strong><\/p>\n<p><a href=\"https:\/\/0xec.blogspot.de\/2016\/08\/labyrenth-ctf-writeup-random-track.html\" rel=\"nofollow,noopener\" >https:\/\/0xec.blogspot.de\/2016\/08\/labyrenth-ctf-writeup-random-track.html<br \/>\n<\/a><a href=\"https:\/\/github.com\/uafio\/git\/blob\/master\/scripts\/labyREnth-2016\/labyrenth-2016-random-2.py\" rel=\"nofollow,noopener\" >https:\/\/github.com\/uafio\/git\/blob\/master\/scripts\/labyREnth-2016\/labyrenth-2016-random-2.py<\/a><\/p>\n<p><strong>Random 3:<\/strong><\/p>\n<p><a href=\"https:\/\/0xec.blogspot.de\/2016\/08\/labyrenth-ctf-writeup-random-track.html\" rel=\"nofollow,noopener\" >https:\/\/0xec.blogspot.de\/2016\/08\/labyrenth-ctf-writeup-random-track.html<br \/>\n<\/a><a href=\"https:\/\/github.com\/uafio\/git\/blob\/master\/scripts\/labyREnth-2016\/labyrenth-2016-random-3.3.py\" rel=\"nofollow,noopener\" >https:\/\/github.com\/uafio\/git\/blob\/master\/scripts\/labyREnth-2016\/labyrenth-2016-random-3.3.py<\/a><\/p>\n<p><strong>Random 4:<\/strong><\/p>\n<p><a href=\"https:\/\/0xec.blogspot.de\/2016\/08\/labyrenth-ctf-writeup-random-track.html\" rel=\"nofollow,noopener\" >https:\/\/0xec.blogspot.de\/2016\/08\/labyrenth-ctf-writeup-random-track.html<br \/>\n<\/a><a href=\"https:\/\/github.com\/uafio\/git\/blob\/master\/scripts\/labyREnth-2016\/labyrenth-2016-random-4.php\" rel=\"nofollow,noopener\" >https:\/\/github.com\/uafio\/git\/blob\/master\/scripts\/labyREnth-2016\/labyrenth-2016-random-4.php<\/a><\/p>\n<p><strong>Random 5:<\/strong><\/p>\n<p><a href=\"https:\/\/0xec.blogspot.de\/2016\/08\/labyrenth-ctf-writeup-random-track.html\" rel=\"nofollow,noopener\" >https:\/\/0xec.blogspot.de\/2016\/08\/labyrenth-ctf-writeup-random-track.html<br \/>\n<\/a><a href=\"https:\/\/github.com\/uafio\/git\/blob\/master\/scripts\/labyREnth-2016\/labyrenth-2016-random-5.2.py\" rel=\"nofollow,noopener\" >https:\/\/github.com\/uafio\/git\/blob\/master\/scripts\/labyREnth-2016\/labyrenth-2016-random-5.2.py<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Welcome back to our blog series where we reveal the\u00a0solutions\u00a0to\u00a0LabyREnth, the Unit 42 Capture the Flag (CTF)\u00a0challenge. Over the last several weeks, we revealed the\u00a0solutions\u00a0for each of the challenge tracks. The time &hellip;<\/p>\n","protected":false},"author":138,"featured_media":20184,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[133],"tags":[2145,2142,586],"coauthors":[1312,2329,1757,1116,1588],"class_list":["post-20703","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-events","tag-ctf","tag-labyrenth","tag-unit-42"],"jetpack_featured_media_url":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-content\/uploads\/2016\/09\/unit42-web-banner-650x300.jpg","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-json\/wp\/v2\/posts\/20703","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-json\/wp\/v2\/users\/138"}],"replies":[{"embeddable":true,"href":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-json\/wp\/v2\/comments?post=20703"}],"version-history":[{"count":11,"href":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-json\/wp\/v2\/posts\/20703\/revisions"}],"predecessor-version":[{"id":20793,"href":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-json\/wp\/v2\/posts\/20703\/revisions\/20793"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-json\/wp\/v2\/media\/20184"}],"wp:attachment":[{"href":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-json\/wp\/v2\/media?parent=20703"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-json\/wp\/v2\/categories?post=20703"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-json\/wp\/v2\/tags?post=20703"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/origin-researchcenter.paloaltonetworks.com\/blog\/wp-json\/wp\/v2\/coauthors?post=20703"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}