├── 34C3 - JuniorsCTF ├── dotr │ ├── README.md │ ├── demo.png │ ├── dotr.py │ └── dotrSolve.py ├── kim │ ├── README.md │ ├── demo.png │ ├── hlextend.py │ ├── kim.py │ └── kimSolve.py └── nohtyp │ ├── README.md │ ├── images │ ├── edit1.png │ ├── edit2.png │ └── edit3.png │ ├── modified.py │ ├── nohtyp.py │ └── solve.py ├── 3DSCTF └── Capo Di Tutti Capi │ ├── README.md │ ├── images │ ├── demo.png │ ├── list.png │ ├── list2.png │ ├── list3.png │ └── page1.png │ └── solve.py ├── CSAW Finals 2017 └── LuPiN │ ├── README.md │ ├── lpn_chal.py │ └── solve.py ├── CSAW Finals 2018 └── Disastrous Security Apparatus │ ├── README.md │ ├── cracker.py │ ├── images │ ├── challenge.png │ └── warning.png │ ├── main.py │ └── solve.py ├── CSAW Quals 2018 ├── Algebra │ ├── README.md │ └── algebra.py └── Take an L │ ├── README.md │ ├── description.pdf │ └── tiling.py ├── DCTF ├── Get Admin │ ├── README.md │ └── flag.png └── Message │ ├── README.md │ └── message.txt ├── Google CTF 2018 ├── JS Safe 2.0 │ ├── README.md │ ├── images │ │ ├── challenge.png │ │ ├── img0.png │ │ ├── img1.png │ │ ├── img2.png │ │ ├── img3.png │ │ ├── img4.png │ │ ├── img5.png │ │ └── img6.png │ ├── js_safe.py │ └── js_safe_2.html └── Shall We Play a Game? │ ├── GameActivity.java │ ├── Modded_GameActivity.smali │ ├── README.md │ ├── app.apk │ ├── images │ ├── challenge.png │ ├── fail.jpg │ ├── flag.jpg │ ├── game.png │ ├── mod.jpg │ └── smali.png │ └── modded.apk ├── RCTF └── Number Game │ ├── README.md │ └── solve.py ├── README.md ├── b00t2root '18 └── Teleport │ ├── README.md │ ├── demo.png │ ├── teleport.py │ └── teleportSolve.py └── b00t2root '19 └── Xorxery ├── solve.py └── xorxery.py /34C3 - JuniorsCTF/dotr/README.md: -------------------------------------------------------------------------------- 1 | # dotr - mid 2 | 3 | I implemented some [crypto](./dotr.py) and encrypted my secret: `03_duCbr5e_i_rY_or cou14:L4G f313_Th_etrph00 Wh03UBl_oo?n07!_e` 4 | 5 | Can you get it back? 6 | 7 | ## Solution 8 | 9 | Script: [dotrSolve.py](./dotrSolve.py) 10 | 11 | TL;DR Bruteforce the key! 12 | 13 | Looking at the encryption we realize that a key consisting of 16 numbers is randomly generated and the first 8 numbers are zipped with numbers from 0 to 7. This zip object is then sorted according to the 1st element in the pair and then **the 2nd elements are used as starting indices to form 8 groups, each containing letters from the message at a distance of 8 from each other starting at the index given by the 2nd element**. These groups are then concatenated and returned as the ciphertext. 14 | 15 | So the encryption is simply a rearrangement of the message according to a key given by a random permutation of the numbers 0-7, applied twice that too with the same key! 16 | 17 | Bruteforcing the key isn't hard as the number of ways to permut 8 numbers is `8!` which is only `40320`. 18 | 19 | Therefore, generate all possible permutations of numbers from 0 to 7 and feed each one as the key to the decryption function twice and check if the final output contains `34C3_`. 20 | 21 | Here's a snippet for splitting the ciphertext into groups for a given key: 22 | 23 | ``` 24 | groups = [] 25 | i = 0 26 | for k in range(8): 27 | grp = [] 28 | tmp = 0 29 | if key[k] < len(ctxt)%8: tmp = 1 30 | for j in range(int(len(ctxt)/8) + tmp): 31 | grp += [ctxt[i+j]] 32 | groups += [grp] 33 | i += j+1 34 | ``` 35 | 36 | Once you formed the groups, the original message can be constructed with the following code: 37 | 38 | ``` 39 | m = ['*']*len(ctxt) 40 | for k in range(8): 41 | i = 0 42 | for j in range(key[k], len(ctxt), 8): 43 | m[j] = groups[k][i] 44 | i += 1 45 | 46 | message = ''.join(m) 47 | ``` 48 | 49 | I ran my script and within a second it showed only two possibilities; out of which one is the flag: `34C3_d0ub1e_Th3_troUBl3_or_n07?!` 50 | 51 |  52 | -------------------------------------------------------------------------------- /34C3 - JuniorsCTF/dotr/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/34C3 - JuniorsCTF/dotr/demo.png -------------------------------------------------------------------------------- /34C3 - JuniorsCTF/dotr/dotr.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | def encrypt(msg, key): 5 | keylen = len(key) 6 | k = [x[1] for x in sorted(zip(key[:keylen], range(keylen)))] 7 | 8 | m = '' 9 | for i in k: 10 | for j in range(i, len(msg), keylen): 11 | m += msg[j] 12 | 13 | return m 14 | 15 | 16 | 17 | m = input() 18 | while True: 19 | k = [random.randrange(256) for _ in range(16)] # generate 2 keys 20 | if len(k) == len(set(k)): 21 | break 22 | 23 | m = encrypt(m, k[:8]) 24 | m = encrypt(m, k[:8]) 25 | 26 | print(m) 27 | 28 | -------------------------------------------------------------------------------- /34C3 - JuniorsCTF/dotr/dotrSolve.py: -------------------------------------------------------------------------------- 1 | 2 | import itertools 3 | 4 | def dec(ctxt, key): 5 | groups = [] 6 | i = 0 7 | 8 | # split the ctxt into 8 groups 9 | for k in range(8): 10 | grp = [] 11 | tmp = 0 12 | if key[k] < len(ctxt)%8: tmp = 1 13 | for j in range(int(len(ctxt)/8) + tmp): 14 | grp += [ctxt[i+j]] 15 | groups += [grp] 16 | i += j+1 17 | 18 | # arrange the letters wrt key 19 | m = ['*']*len(ctxt) 20 | for k in range(8): 21 | i = 0 22 | for j in range(key[k], len(ctxt), 8): 23 | m[j] = groups[k][i] 24 | i += 1 25 | 26 | return ''.join(m) 27 | 28 | ctxt = "03_duCbr5e_i_rY_or cou14:L4G f313_Th_etrph00 Wh03UBl_oo?n07!_e" 29 | allPossibleKeys = list(itertools.permutations([0, 1, 2, 3, 4, 5, 6, 7])) 30 | print('Possible Flags: ') 31 | 32 | # bruteforce the key 33 | for key in allPossibleKeys: 34 | m = dec(ctxt, key) 35 | m = dec(m, key) 36 | if "34C3_" in m: 37 | print(m) 38 | -------------------------------------------------------------------------------- /34C3 - JuniorsCTF/kim/README.md: -------------------------------------------------------------------------------- 1 | # kim - easy 2 | 3 | Check [this](http://35.198.133.163:1337/) out!!!!!!!!!!!\x80\x00.... 4 | 5 | Update: [Source](./kim.py) 6 | 7 | 8 | ## Solution 9 | 10 | Script: [kimSolve.py](./kimSolve.py) 11 | 12 | TL;DR Perform a hash length extension attack on sha1 13 | 14 | The question requires us to send a query containing the parameter `'f=flag.png'` to the url given by the sha1 hash of the query itself. Problem is that the sha1 hash is salted and we don't know the salt. However, we are provided with a sample query (f=sample.gif) and its salted hash(952bb2a215b032abe27d24296be099dc3334755c). 15 | 16 | #### About the Attack 17 | For performing a hash length extension attack, we require a string (say `query`) and it's corresponding salted hash (say `saltedHash`). Then, supposing we only know the length of the salt (say `saltLen`), it is possible to construct another `newSaltedHash` and its corresponding string `newQuery` which starts with the contents of `query` and ends with a string of our choice (say `newKeyValue`). 18 | 19 | In other words: `newQuery, newSaltedHash = hashLengthExtensionAttack(newKeyValue, query, saltLen, saltedHash)` 20 | 21 | #### Performing the Attack 22 | In this case, we can construct a query such that it begins with `?f=sample.gif` and ends with `f=flag`, something like `?f=sample.gif&XXXXXXXXXXX&f=flag`. 23 | Such a query would give preference to the latter value assigning the value `flag` to the key `f`. So we have the following: 24 | ``` 25 | query = 'f=sample.gif' 26 | saltedHash = '952bb2a215b032abe27d24296be099dc3334755c' 27 | newKeyValue = 'f=flag' 28 | ``` 29 | 30 | [This](https://github.com/stephenbradshaw/hlextend) is a useful tool I found to perform the necessary attack. We do not know the length of the salt but it can be bruteforced easily. I wrote a script to do exactly this with the help of the hlextend module. After running the script, for salt length = 15, we get the flag: `34C3_a11_y0u_ne3d_is_puMp_and_dump` 31 | 32 |  33 | -------------------------------------------------------------------------------- /34C3 - JuniorsCTF/kim/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/34C3 - JuniorsCTF/kim/demo.png -------------------------------------------------------------------------------- /34C3 - JuniorsCTF/kim/hlextend.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2014 by Stephen Bradshaw 2 | # 3 | # SHA1 and SHA2 generation routines from SlowSha https://code.google.com/p/slowsha/ 4 | # which is: Copyright (C) 2011 by Stefano Palazzo 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | 24 | ''' 25 | Pure Python Hash Length Extension module. 26 | 27 | Currently supports SHA1, SHA256 and SHA512, more algorithms will 28 | be added in the future. 29 | 30 | 31 | Create a hash by calling one of the named constuctor functions: 32 | sha1(), sha256(), and sha512(), or by calling new(algorithm). 33 | 34 | The hash objects have the following methods: 35 | 36 | hash(message): 37 | 38 | Feeds data into the hash function using the normal interface. 39 | 40 | extend(appendData, knownData, secretLength, startHash, raw=False): 41 | 42 | Performs a hash length extension attack. Returns the string to 43 | use when appending data. 44 | 45 | hexdigest(): 46 | 47 | Returns a hexlified version of the hash output. 48 | 49 | 50 | Assume you have a hash generated from an unknown secret value concatenated with 51 | a known value, and you want to be able to produce a valid hash after appending 52 | additional data to the known value. 53 | 54 | If the hash algorithm used is one of the vulnerable functions implemented in 55 | this module, is is possible to achieve this without knowing the secret value 56 | as long as you know (or can guess, perhaps by brute force) the length of that 57 | secret value. This is called a hash length extension attack. 58 | 59 | 60 | Given an existing sha1 hash value '52e98441017043eee154a6d1af98c5e0efab055c', 61 | known data of 'hello', an unknown secret of length 10 and data you wish 62 | to append of 'file', you would do the following to perform the attack: 63 | 64 | >>> import hlextend 65 | >>> sha = hlextend.new('sha1') 66 | >>> print sha.extend('file', 'hello', 10, '52e98441017043eee154a6d1af98c5e0efab055c') 67 | 'hello\\x80\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00 68 | \\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00 69 | \\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00xfile' 70 | >>> print sha.hexdigest() 71 | c60fa7de0860d4048a3bfb36b70299a95e6587c9 72 | 73 | The unknown secret (of length 10), that when hashed appended with 'hello' produces 74 | a SHA1 hash of '52e98441017043eee154a6d1af98c5e0efab055c', will then produce 75 | a SHA1 hash of 'c60fa7de0860d4048a3bfb36b70299a95e6587c9' when appended with the output 76 | from the extend function above. 77 | 78 | If you are not sure of the exact length of the secret value, simply try the above 79 | multiple times specifying different values for the length to brute force. 80 | 81 | ''' 82 | 83 | 84 | 85 | from re import match 86 | from math import ceil 87 | 88 | 89 | __version__ = "0.1" 90 | 91 | 92 | 93 | class Hash(object): 94 | '''Parent class for hash functions''' 95 | 96 | 97 | def hash(self, message): 98 | '''Normal input for data into hash function''' 99 | 100 | length = bin(len(message) * 8)[2:].rjust(self._blockSize, "0") 101 | 102 | while len(message) > self._blockSize: 103 | self._transform(''.join([bin(ord(a))[2:].rjust(8, "0") for a in message[:self._blockSize]])) 104 | message = message[self._blockSize:] 105 | 106 | message = self.__hashBinaryPad(message, length) 107 | 108 | 109 | for a in xrange(len(message) // self._b2): 110 | self._transform(message[a * self._b2:a * self._b2 + self._b2]) 111 | 112 | 113 | 114 | def extend(self, appendData, knownData, secretLength, startHash, raw=False): 115 | '''Hash length extension input for data into hash function''' 116 | 117 | self.__checkInput(secretLength, startHash) 118 | self.__setStartingHash(startHash) 119 | 120 | extendLength = self.__hashGetExtendLength(secretLength, knownData, appendData) 121 | 122 | message = appendData 123 | 124 | while len(message) > self._blockSize: 125 | self._transform(''.join([bin(ord(a))[2:].rjust(8, "0") for a in message[:self._blockSize]])) 126 | message = message[self._blockSize:] 127 | 128 | message = self.__hashBinaryPad(message, extendLength) 129 | 130 | for i in xrange(len(message) // self._b2): 131 | self._transform(message[i * self._b2:i * self._b2 + self._b2]) 132 | 133 | return self.__hashGetPadData(secretLength, knownData, appendData, raw=raw) 134 | 135 | 136 | def hexdigest(self): 137 | '''Outputs hash data in hexlified format''' 138 | return ''.join( [ (('%0' + str(self._b1) + 'x') % (a)) for a in self.__digest()]) 139 | 140 | 141 | def __init__(self): 142 | # pre calculate some values that get used a lot 143 | self._b1 = self._blockSize/8 144 | self._b2 = self._blockSize*8 145 | 146 | 147 | 148 | def __digest(self): 149 | return [self.__getattribute__(a) for a in dir(self) if match('^_h\d+$', a)] 150 | 151 | 152 | def __setStartingHash(self, startHash): 153 | c = 0 154 | hashVals = [ int(startHash[a:a+self._b1],base=16) for a in range(0,len(startHash), self._b1) ] 155 | for hv in [ a for a in dir(self) if match('^_h\d+$', a) ]: 156 | self.__setattr__(hv, hashVals[c]) 157 | c+=1 158 | 159 | 160 | def __checkInput(self, secretLength, startHash): 161 | if not isinstance(secretLength, int): 162 | raise TypeError('secretLength must be a valid integer') 163 | if secretLength < 1: 164 | raise ValueError('secretLength must be grater than 0') 165 | if not match('^[a-fA-F0-9]{' + str(len(self.hexdigest())) + '}$', startHash): 166 | raise ValueError('startHash must be a string of length ' + str(len(self.hexdigest())) + ' in hexlified format') 167 | 168 | 169 | def __byter(self, byteVal): 170 | '''Helper function to return usable values for hash extension append data''' 171 | if byteVal < 0x20 or byteVal > 0x7e: 172 | return '\\x%02x' %(byteVal) 173 | else: 174 | return chr(byteVal) 175 | 176 | 177 | def __binToByte(self, binary): 178 | '''Convert a binary string to a byte string''' 179 | return ''.join([ chr(int(binary[a:a+8],base=2)) for a in xrange(0,len(binary),8) ]) 180 | 181 | 182 | 183 | def __hashGetExtendLength(self, secretLength, knownData, appendData): 184 | '''Length function for hash length extension attacks''' 185 | # binary length (secretLength + len(knownData) + size of binarysize+1) rounded to a multiple of blockSize + length of appended data 186 | originalHashLength = int(ceil((secretLength+len(knownData)+self._b1+1)/float(self._blockSize)) * self._blockSize) 187 | newHashLength = originalHashLength + len(appendData) 188 | return bin(newHashLength * 8)[2:].rjust(self._blockSize, "0") 189 | 190 | 191 | def __hashGetPadData(self, secretLength, knownData, appendData, raw=False): 192 | '''Return append value for hash extension attack''' 193 | originalHashLength = bin((secretLength+len(knownData)) * 8)[2:].rjust(self._blockSize, "0") 194 | padData = ''.join(bin(ord(i))[2:].rjust(8, "0") for i in knownData) + "1" 195 | padData += "0" * (((self._blockSize*7) - (len(padData)+(secretLength*8)) % self._b2) % self._b2) + originalHashLength 196 | if not raw: 197 | return ''.join([ self.__byter(int(padData[a:a+8],base=2)) for a in xrange(0,len(padData),8) ]) + appendData 198 | else: 199 | return self.__binToByte(padData) + appendData 200 | 201 | 202 | def __hashBinaryPad(self, message, length): 203 | '''Pads the final blockSize block with \x80, zeros, and the length, converts to binary''' 204 | message = ''.join(bin(ord(i))[2:].rjust(8, "0") for i in message) + "1" 205 | message += "0" * (((self._blockSize*7) - len(message) % self._b2) % self._b2) + length 206 | return message 207 | 208 | 209 | 210 | 211 | class SHA1 (Hash): 212 | 213 | _h0, _h1, _h2, _h3, _h4, = ( 214 | 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0) 215 | 216 | _blockSize = 64 217 | 218 | 219 | def _transform(self, chunk): 220 | 221 | lrot = lambda x, n: (x << n) | (x >> (32 - n)) 222 | w = [] 223 | 224 | for j in xrange(len(chunk) // 32): 225 | w.append(int(chunk[j * 32:j * 32 + 32], 2)) 226 | 227 | for i in xrange(16, 80): 228 | w.append(lrot(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1) 229 | & 0xffffffff) 230 | 231 | a = self._h0 232 | b = self._h1 233 | c = self._h2 234 | d = self._h3 235 | e = self._h4 236 | 237 | for i in xrange(80): 238 | 239 | if i <= i <= 19: 240 | f, k = d ^ (b & (c ^ d)), 0x5a827999 241 | elif 20 <= i <= 39: 242 | f, k = b ^ c ^ d, 0x6ed9eba1 243 | elif 40 <= i <= 59: 244 | f, k = (b & c) | (d & (b | c)), 0x8f1bbcdc 245 | elif 60 <= i <= 79: 246 | f, k = b ^ c ^ d, 0xca62c1d6 247 | 248 | temp = lrot(a, 5) + f + e + k + w[i] & 0xffffffff 249 | a, b, c, d, e = temp, a, lrot(b, 30), c, d 250 | 251 | self._h0 = (self._h0 + a) & 0xffffffff 252 | self._h1 = (self._h1 + b) & 0xffffffff 253 | self._h2 = (self._h2 + c) & 0xffffffff 254 | self._h3 = (self._h3 + d) & 0xffffffff 255 | self._h4 = (self._h4 + e) & 0xffffffff 256 | 257 | 258 | 259 | class SHA256 (Hash): 260 | 261 | _h0, _h1, _h2, _h3, _h4, _h5, _h6, _h7 = ( 262 | 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 263 | 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19) 264 | 265 | _blockSize = 64 266 | 267 | 268 | def _transform(self, chunk): 269 | rrot = lambda x, n: (x >> n) | (x << (32 - n)) 270 | w = [] 271 | 272 | k = [ 273 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 274 | 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 275 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 276 | 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 277 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 278 | 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 279 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 280 | 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 281 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 282 | 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 283 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 284 | 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 285 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 286 | 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 287 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 288 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2] 289 | 290 | for j in xrange(len(chunk) // 32): 291 | w.append(int(chunk[j * 32:j * 32 + 32], 2)) 292 | 293 | for i in xrange(16, 64): 294 | s0 = rrot(w[i - 15], 7) ^ rrot(w[i - 15], 18) ^ (w[i - 15] >> 3) 295 | s1 = rrot(w[i - 2], 17) ^ rrot(w[i - 2], 19) ^ (w[i - 2] >> 10) 296 | w.append((w[i - 16] + s0 + w[i - 7] + s1) & 0xffffffff) 297 | 298 | a = self._h0 299 | b = self._h1 300 | c = self._h2 301 | d = self._h3 302 | e = self._h4 303 | f = self._h5 304 | g = self._h6 305 | h = self._h7 306 | 307 | for i in xrange(64): 308 | s0 = rrot(a, 2) ^ rrot(a, 13) ^ rrot(a, 22) 309 | maj = (a & b) ^ (a & c) ^ (b & c) 310 | t2 = s0 + maj 311 | s1 = rrot(e, 6) ^ rrot(e, 11) ^ rrot(e, 25) 312 | ch = (e & f) ^ ((~ e) & g) 313 | t1 = h + s1 + ch + k[i] + w[i] 314 | 315 | h = g 316 | g = f 317 | f = e 318 | e = (d + t1) & 0xffffffff 319 | d = c 320 | c = b 321 | b = a 322 | a = (t1 + t2) & 0xffffffff 323 | 324 | self._h0 = (self._h0 + a) & 0xffffffff 325 | self._h1 = (self._h1 + b) & 0xffffffff 326 | self._h2 = (self._h2 + c) & 0xffffffff 327 | self._h3 = (self._h3 + d) & 0xffffffff 328 | self._h4 = (self._h4 + e) & 0xffffffff 329 | self._h5 = (self._h5 + f) & 0xffffffff 330 | self._h6 = (self._h6 + g) & 0xffffffff 331 | self._h7 = (self._h7 + h) & 0xffffffff 332 | 333 | 334 | 335 | 336 | class SHA512 (Hash): 337 | 338 | _h0, _h1, _h2, _h3, _h4, _h5, _h6, _h7 = ( 339 | 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 340 | 0xa54ff53a5f1d36f1, 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 341 | 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179) 342 | 343 | _blockSize = 128 344 | 345 | 346 | def _transform(self, chunk): 347 | 348 | rrot = lambda x, n: (x >> n) | (x << (64 - n)) 349 | w = [] 350 | 351 | k = [ 352 | 0x428a2f98d728ae22, 0x7137449123ef65cd, 353 | 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 354 | 0x3956c25bf348b538, 0x59f111f1b605d019, 355 | 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 356 | 0xd807aa98a3030242, 0x12835b0145706fbe, 357 | 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 358 | 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 359 | 0x9bdc06a725c71235, 0xc19bf174cf692694, 360 | 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 361 | 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, 362 | 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 363 | 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 364 | 0x983e5152ee66dfab, 0xa831c66d2db43210, 365 | 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 366 | 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 367 | 0x06ca6351e003826f, 0x142929670a0e6e70, 368 | 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 369 | 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, 370 | 0x650a73548baf63de, 0x766a0abb3c77b2a8, 371 | 0x81c2c92e47edaee6, 0x92722c851482353b, 372 | 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 373 | 0xc24b8b70d0f89791, 0xc76c51a30654be30, 374 | 0xd192e819d6ef5218, 0xd69906245565a910, 375 | 0xf40e35855771202a, 0x106aa07032bbd1b8, 376 | 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 377 | 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 378 | 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 379 | 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, 380 | 0x748f82ee5defb2fc, 0x78a5636f43172f60, 381 | 0x84c87814a1f0ab72, 0x8cc702081a6439ec, 382 | 0x90befffa23631e28, 0xa4506cebde82bde9, 383 | 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 384 | 0xca273eceea26619c, 0xd186b8c721c0c207, 385 | 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 386 | 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 387 | 0x113f9804bef90dae, 0x1b710b35131c471b, 388 | 0x28db77f523047d84, 0x32caab7b40c72493, 389 | 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, 390 | 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 391 | 0x5fcb6fab3ad6faec, 0x6c44198c4a475817] 392 | 393 | for j in xrange(len(chunk) // 64): 394 | w.append(int(chunk[j * 64:j * 64 + 64], 2)) 395 | 396 | for i in xrange(16, 80): 397 | s0 = rrot(w[i - 15], 1) ^ rrot(w[i - 15], 8) ^ (w[i - 15] >> 7) 398 | s1 = rrot(w[i - 2], 19) ^ rrot(w[i - 2], 61) ^ (w[i - 2] >> 6) 399 | w.append((w[i - 16] + s0 + w[i - 7] + s1) & 0xffffffffffffffff) 400 | 401 | a = self._h0 402 | b = self._h1 403 | c = self._h2 404 | d = self._h3 405 | e = self._h4 406 | f = self._h5 407 | g = self._h6 408 | h = self._h7 409 | 410 | for i in xrange(80): 411 | s0 = rrot(a, 28) ^ rrot(a, 34) ^ rrot(a, 39) 412 | maj = (a & b) ^ (a & c) ^ (b & c) 413 | t2 = s0 + maj 414 | s1 = rrot(e, 14) ^ rrot(e, 18) ^ rrot(e, 41) 415 | ch = (e & f) ^ ((~ e) & g) 416 | t1 = h + s1 + ch + k[i] + w[i] 417 | 418 | h = g 419 | g = f 420 | f = e 421 | e = (d + t1) & 0xffffffffffffffff 422 | d = c 423 | c = b 424 | b = a 425 | a = (t1 + t2) & 0xffffffffffffffff 426 | 427 | self._h0 = (self._h0 + a) & 0xffffffffffffffff 428 | self._h1 = (self._h1 + b) & 0xffffffffffffffff 429 | self._h2 = (self._h2 + c) & 0xffffffffffffffff 430 | self._h3 = (self._h3 + d) & 0xffffffffffffffff 431 | self._h4 = (self._h4 + e) & 0xffffffffffffffff 432 | self._h5 = (self._h5 + f) & 0xffffffffffffffff 433 | self._h6 = (self._h6 + g) & 0xffffffffffffffff 434 | self._h7 = (self._h7 + h) & 0xffffffffffffffff 435 | 436 | 437 | 438 | 439 | 440 | def new(algorithm): 441 | obj = { 442 | 'sha1': SHA1, 443 | 'sha256': SHA256, 444 | 'sha512': SHA512, 445 | }[algorithm]() 446 | return obj 447 | 448 | 449 | 450 | def sha1(): 451 | ''' Returns a new sha1 hash object ''' 452 | return new('sha1') 453 | 454 | 455 | 456 | def sha256(): 457 | ''' Returns a new sha256 hash object ''' 458 | return new('sha256', ) 459 | 460 | 461 | 462 | def sha512(): 463 | ''' Returns a new sha512 hash object ''' 464 | return new('sha512', ) 465 | 466 | 467 | 468 | __all__ = ('sha1', 'sha256', 'sha512') 469 | 470 | 471 | -------------------------------------------------------------------------------- /34C3 - JuniorsCTF/kim/kim.py: -------------------------------------------------------------------------------- 1 | from bottle import route, run, template, request, redirect, static_file 2 | import hashlib 3 | SECRET = "hello" 4 | 5 | INDEX = '
Download a sample here!' 6 | FILES = '
>i) for i in range(8)] for c in s])) 17 | pack = lambda x: (lambda t: ''.join(chr(sum(t[8*i:8*i+8])) for i in range(len(t)/8)))([(b<<(i%8)) for i,b in enumerate(x)]) 18 | serialize_mat = lambda X: '%s;%s' % (','.join(str(i) for i in X.shape), pack(X.reshape((product(X.shape),))).encode('base64')) 19 | deserialize_mat = lambda s: (lambda (dims,data): np.array(unpack(data.decode('base64'))).reshape(map(int,dims.split(','))))(s.split(';')) 20 | 21 | def MultiBitKeyGen(n): 22 | t = 1.0/n 23 | S = randbitmat((n, n)) 24 | A = randbitmat((2*n, n)) 25 | E = ber((2*n, n), t) 26 | B = (A.dot(S) + E) % 2 27 | return {'pk': (A, B, t), 'sk': S} 28 | 29 | def MultiBitEnc((A, B, t), s): 30 | n = A.shape[1] 31 | v = unpack(s) 32 | f = ber((2*n,), t) 33 | u = f.T.dot(A) % 2 34 | c = (f.T.dot(B) + np.array(v+[0]*(n-len(v))).T) % 2 35 | return (u, c) 36 | 37 | def MultiBitDec(S, (u,c)): 38 | d = (c + S.T.dot(u)).T % 2 39 | return pack(d) 40 | 41 | def serialize_key(key): 42 | (A,B,t) = key['pk'] 43 | S = key['sk'] 44 | ser = {'pk': (serialize_mat(A), serialize_mat(B), t), 'sk': serialize_mat(S)} 45 | return json.dumps(ser) 46 | 47 | def get_values(m): 48 | rs_bytes = 20 49 | codec = reedsolo.RSCodec(rs_bytes) 50 | try: 51 | with open("lpn.key", "r") as f: 52 | key = json.loads(f.read()) 53 | key['pk'][0] = deserialize_mat(key['pk'][0]) 54 | key['pk'][1] = deserialize_mat(key['pk'][1]) 55 | key['sk'] = deserialize_mat(key['sk']) 56 | except IOError as e: 57 | assert e.strerror == 'No such file or directory' 58 | key = MultiBitKeyGen(800) 59 | with open("lpn.key", "w") as f: 60 | f.write(serialize_key(key)) 61 | #print key 62 | ctxt = MultiBitEnc(key['pk'], bytes(codec.encode(m))) 63 | #print ctxt 64 | ptxt = codec.decode(bytearray(MultiBitDec(key['sk'], ctxt))) 65 | #print ptxt 66 | return (key, ctxt, ptxt) 67 | 68 | if __name__ == '__main__': 69 | #assert get_values("A"*20)[2][:20] == "A"*20 70 | key, ctxt, ptxt = get_values(flag) 71 | print("A: %r\nB: %r\nu: %r\nc: %r" % tuple(map(serialize_mat, (key['pk'][0], key['pk'][1], ctxt[0], ctxt[1])))) 72 | -------------------------------------------------------------------------------- /CSAW Finals 2017/LuPiN/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | import numpy as np 3 | import socket 4 | from ast import literal_eval 5 | 6 | # Taken as it is from lpn_chal.py 7 | unpack = lambda s: list(__import__('itertools').chain(*[[1&(ord(c)>>i) for i in range(8)] for c in s])) 8 | pack = lambda x: (lambda t: ''.join(chr(sum(t[8*i:8*i+8])) for i in range(len(t)/8)))([(b<<(i%8)) for i,b in enumerate(x)]) 9 | deserialize_mat = lambda s: (lambda (dims,data): np.array(unpack(data.decode('base64'))).reshape(map(int,dims.split(','))))(s.split(';')) 10 | 11 | # The actual decryption method ;) 12 | def crackTheCipher(A, B, u, c): 13 | fT = u.dot(np.linalg.pinv(A)) % 2 14 | v = np.int_(c - fT.dot(B)) % 2 15 | flag = pack(v) 16 | return flag 17 | 18 | # To get the data from the live server and write to a file 19 | def nc(): 20 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 21 | s.connect(("crypto.chal.csaw.io", 1922)) 22 | f = open("lpnData","w") 23 | while 1: 24 | data = s.recv(1024) 25 | if data == "": 26 | break 27 | f.write(data) 28 | f.close() 29 | 30 | # To get the data from local lpn_chal.py instead of the live server 31 | def sim_nc(): 32 | import subprocess 33 | with open("lpnData", "w+") as output: 34 | subprocess.call(["python", "./lpn_chal.py"], stdout=output); 35 | 36 | # Reads the variables from the file 37 | def readFile(): 38 | f = open("lpnData", "r") 39 | As = literal_eval(f.readline()[3:]) 40 | Bs = literal_eval(f.readline()[3:]) 41 | us = literal_eval(f.readline()[3:]) 42 | cs = literal_eval(f.readline()[3:]) 43 | f.close() 44 | return As, Bs, us, cs 45 | 46 | if __name__ == '__main__': 47 | print("Decrypting...") 48 | while True: 49 | #nc() 50 | sim_nc() 51 | As, Bs, us, cs = readFile() 52 | 53 | As = deserialize_mat(As) 54 | Bs = deserialize_mat(Bs) 55 | us = deserialize_mat(us) 56 | cs = deserialize_mat(cs) 57 | 58 | flag = crackTheCipher(As, Bs, us, cs) 59 | 60 | if(flag.startswith("flag{")): 61 | print("Success!\n\n" + flag.split("}")[0] + "}\n") 62 | break 63 | else: 64 | print("Attempting decryption again...") 65 | -------------------------------------------------------------------------------- /CSAW Finals 2018/Disastrous Security Apparatus/README.md: -------------------------------------------------------------------------------- 1 | # Disastrous Security Apparatus - 400pts 2 | 3 | Here's the challenge task from the crypto category: 4 | 5 |  6 | 7 | 8 | Good Luck, k? 9 | 10 | Author: Paul Kehrer, Trail of Bits. 11 | 12 | [http://crypto.chal.csaw.io:1000](http://crypto.chal.csaw.io:1000) 13 | 14 | Update: Please message us (@tnek or @ghost) on IRC if you have a solver for this challenge that works locally but doesn't work remotely. 15 | 16 | Files: [main.py](./main.py) 17 | 18 | 19 | 20 | ## The Overview 21 | 22 | The title of the challenge hints us that this question is about DSA or Digital Signature Algorithm. 23 | DSA is a public-key cryptosystem where the messages are signed by the signer's private key (`x`) and the signatures are verified by the signer's corresponding public key (`y`). 24 | Downloading the given source code, we realize it is a simple Flask application with 7 routes defined: 25 | 26 | - `/` 27 | 28 | The index page greeting us 29 | 30 | - `/public_key` 31 | 32 | Displays the DSA public key of the server listing `p`, `q`, `g` and `y` 33 | 34 | - `/challenge` 35 | 36 | Displays the Fernet encrypted ciphertext of the word `challenged!` 37 | 38 | - `/sign/` 39 | 40 | Signs the given `data` with the private key on the server using the SHA1 hashing algorithm 41 | 42 | - `/capture` 43 | 44 | This endpoint only accepts POST requests with `challenge` and `signature` as inputs. It first Fernet decrypts the ciphertext given in `challenge` and then verifies its signature against the given one using SHA256 hashing algorithm. 45 | 46 | If there's no error in the above steps, the flag is displayed to us! 47 | 48 | - `/forgotpass` 49 | 50 | Displays a url with a random 64-bit number at the end 51 | 52 | - `/resetpass/` 53 | 54 | Hasn't been implemented yet 55 | 56 | It's clear that we'll get the flag only if we can produce a valid signature of any data signed using SHA-2. Unfortunately we only know the signature signed using SHA-1 obtained via `/challenge`. We somehow need to forge a valid signature and get the flag. 57 | 58 | I immediately downloaded the code and set up a local server for faster responses and easy debugging. I went through the code once and noticed some endpoints were unnecessary like `/`, `/resetpass`, and `/forgotpass` because there was no `login` system right, why do we need these features? To simplify the challenge, I removed these but put them at the back of my head (which turns out to be useful later!). 59 | 60 | My friend and I tried doing the math to somehow manipulate the equations used for signing and verifying and create a fake signature but it basically boiled down to bruteforcing the private key `x` which isn't feasible. I went to the Wikipedia page for [Digital Signature Algorithm](https://en.wikipedia.org/wiki/Digital_Signature_Algorithm) and read that the random value `k` used in generating the signature is very critical to the signing algorithm. 61 | 62 | Apparently the ECDSA private key used by Sony to sign software for the PlayStation 3 game console was easily recovered because they didn't use a new random key for each signature. 63 | 64 | Cool! So maybe there's a way to predict the value of `k` during signing. The challenge uses python's random module to generate random numbers and visiting the docs of the module, I notice a warning: 65 | 66 |  67 | 68 | That means the random module is breakable! Searching around for a bit, I realize the PRNG is based on [`Mersenne Twister`](https://en.wikipedia.org/wiki/Mersenne_Twister) which is a general-purpose PRNG and not exactly cryptographically secure. Then I come across this very useful repo on GitHub: [https://github.com/tna0y/Python-random-module-cracker](https://github.com/tna0y/Python-random-module-cracker). 69 | 70 | It basically takes 612 32-bit random numbers and predicts the internal state matrix of the PRNG and outputs the most probable random numbers that might generate next. Ohh! So this is where the `/forgotpass` will be useful! The random value at the end of the resetpass link can be given as input to this script to predict the value of `k`! 71 | 72 | ## The Solution 73 | 74 | **Script: [solve.py](solve.py)** 75 | 76 | I cloned the repo, imported it into my script and coded up a scraper that feeds the random numbers from `/forgotpass` to the Cracker. But there was a slight problem. The Cracker is for 32-bit random numbers and the challenge deals only with 64-bit numbers. My friend and I searched for a bit and switched the Mersenne Twister coeffecients in `RandCrack` with their 64-bit counterparts but it didn't seem to work. Then I had the following hunch: *What if there's a relation between 2 consecutive 32-bit numbers and 1 64-bit number generated from the same seed?* I put this theory to test: 77 | 78 | ```py 79 | >>> import random 80 | >>> import time 81 | >>> seed = time.time() 82 | >>> random.seed(seed) 83 | >>> r1 = random.getrandbits(32) 84 | >>> r2 = random.getrandbits(32) 85 | >>> random.seed(seed) 86 | >>> n = random.getrandbits(64) 87 | >>> n == (r1 << 32) + r2 88 | False 89 | >>> n == (r2 << 32) + r1 90 | True 91 | ``` 92 | 93 | The second statement returned `True`! Great! So they are indeed related via bit-shift and addition! Now we can continue working on the Cracker and use this relation wherever needed. Here's the function which feeds the random numbers to the cracker and predicts `k`. It takes the an object of `RandCrack` and the Fernet ciphertext returned by `/challenge`: 94 | 95 | ```py 96 | def get_numbers(cracker, data): 97 | l = [requests.get(base+'/forgotpass').text for _ in range(312)] 98 | d = eval(requests.get(base+'/sign/'+data).text) 99 | r, s = d['r'], d['s'] 100 | pk = eval(requests.get(base+'/public_key').text) 101 | 102 | for i in range(312): 103 | n = l[i].split('/')[-1] 104 | n = binascii.unhexlify(n) 105 | n = struct.unpack('>Q', n)[0] 106 | r1 = n & (0xffffffff) 107 | r2 = (n & (0xffffffff00000000)) >> 32 108 | assert (n == ((r2 << 32) + r1)) 109 | cracker.submit(r1) 110 | cracker.submit(r2) 111 | k = cracker.predict_randrange(2, pk['q']) 112 | return pk, r, s, k 113 | ``` 114 | 115 | It returns the `public_key`, `r`, `s` and *hopefully* the correctly predicted `k`. Now we can do some math and get the private key `x` from this data. We know that: 116 | ``` 117 | s = kinv * (h + r * x) % q 118 | s * k = (h + r * x) % q 119 | (s * k) % q = (h + r * x) % q 120 | (s * k - h) % q = (r * x) % q 121 | ((s * k - h) * rinv) % q = x % q 122 | ``` 123 | Hence, 124 | ``` 125 | x = ((s * k - h) * rinv) % q 126 | ``` 127 | 128 | Here `kinv` and `rinv` are the modular inverses of `k` and `r` respectively wrt. `q`. We can verify this value of `x` by checking if `y == g ** x % q` or `y == pow(g, x, q)`. If it returns `True` then we've successfully extracted the private key and can now forge a valid signature for any data using any hash. 129 | 130 | Let's use the same `r` and same `data` (the Fernet ciphertext) but let's sign it using SHA-256. We get the new value of `s` as: 131 | 132 | `forge_s = kinv * (h2 + r * x) % q` 133 | 134 | Here `h2` is the SHA-256 sum of `data` interpreted as an integer. 135 | We can use the `encode_dss_signature()` function to encode `(r, forge_s)` into a signature and `POST` this value along with the `data` containing the corresponding Fernet ciphertext to the `/capture` endpoint and get the flag! 136 | 137 | **`flag{NowyourereadytocrackthePS3YeahSonydidthiswithECDSA}`** 138 | 139 | P.S. 140 | - You can set-up the challenge on your `localhost` by generating your own DSA-key or by simply copying this [ctf.key](https://github.com/osirislab/CSAW-CTF-2018-Finals/blob/master/crypto/distastrous_security_apparatus/ctf.key) to your directory, setting the `CSAW_FLAG` environment variable and running [main.py](main.py) 141 | - During the challenge, the solution worked on localhost but not remotely so when I contacted the admin as the Update suggested, they gave me a different url on which the script worked perfectly and we got the flag. 142 | -------------------------------------------------------------------------------- /CSAW Finals 2018/Disastrous Security Apparatus/cracker.py: -------------------------------------------------------------------------------- 1 | 2 | class RandCrack: 3 | 4 | def __init__(self): 5 | self.counter = 0 6 | self.mt = [] 7 | self.state = False 8 | 9 | def submit(self, num): 10 | if self.state: 11 | print("Already got enough bits") 12 | return 13 | bits = self._to_bitarray(num) 14 | 15 | assert(all([x == 0 or x == 1 for x in bits])) 16 | self.counter +=1 17 | self.mt.append(self._harden_inverse(bits)) 18 | if self.counter == 624: 19 | self._regen() 20 | self.state = True 21 | 22 | def _predict_32(self): 23 | if not self.state: 24 | print("Didn't recieve enough bits to predict") 25 | return 0 26 | if self.counter >= 624: 27 | self._regen() 28 | self.counter += 1 29 | 30 | return self._harden(self.mt[self.counter-1]) 31 | 32 | def predict_getrandbits(self,k): 33 | if not self.state: 34 | print("Didn't recieve enough bits to predict") 35 | return 0 36 | if k == 0: 37 | return 0 38 | words = (k-1) // 32 + 1 39 | res = [] 40 | for i in range(words): 41 | r = self._predict_32() 42 | if k < 32: 43 | r = [0]*(32-k) +r[:k] 44 | res = r + res 45 | k -= 32 46 | return self._to_int(res) 47 | 48 | def predict_randbelow(self, n): 49 | k = n.bit_length() 50 | r = self.predict_getrandbits(k) 51 | while r >= n: 52 | r = self.predict_getrandbits(k) 53 | return r 54 | 55 | def predict_randrange(self, start, stop=None, step=1, _int=int): 56 | # Adopted messy code from random.py module 57 | # In fact only changed _randbelow() method calls to predict_randbelow() 58 | istart = _int(start) 59 | if istart != start: 60 | raise ValueError("non-integer arg 1 for randrange()") 61 | if stop is None: 62 | if istart > 0: 63 | return self.predict_randbelow(istart) 64 | raise ValueError("empty range for randrange()") 65 | 66 | # stop argument supplied. 67 | istop = _int(stop) 68 | if istop != stop: 69 | raise ValueError("non-integer stop for randrange()") 70 | width = istop - istart 71 | if step == 1 and width > 0: 72 | return istart + self.predict_randbelow(width) 73 | if step == 1: 74 | raise ValueError("empty range for randrange() (%d,%d, %d)" % (istart, istop, width)) 75 | 76 | # Non-unit step argument supplied. 77 | istep = _int(step) 78 | if istep != step: 79 | raise ValueError("non-integer step for randrange()") 80 | if istep > 0: 81 | n = (width + istep - 1) // istep 82 | elif istep < 0: 83 | n = (width + istep + 1) // istep 84 | else: 85 | raise ValueError("zero step for randrange()") 86 | 87 | if n <= 0: 88 | raise ValueError("empty range for randrange()") 89 | 90 | return istart + istep*self.predict_randbelow(n) 91 | 92 | def predict_randint(self, a,b): 93 | return self.predict_randrange(a, b+1) 94 | 95 | def predict_choice(self, seq): 96 | try: 97 | i = self.predict_randbelow(len(seq)) 98 | except ValueError: 99 | raise IndexError('Cannot choose from an empty sequence') 100 | return seq[i] 101 | 102 | def _to_bitarray(self, num): 103 | k = [int(x) for x in bin(num)[2:]] 104 | return [0] * (32-len(k)) + k 105 | 106 | def _to_int(self, bits ): 107 | return int("".join(str(i) for i in bits), 2) 108 | 109 | def _or_nums(self, a, b): 110 | if len(a) < 32: 111 | a = [0]* (32-len(a))+a 112 | if len(b) < 32: 113 | b = [0]* (32-len(b))+b 114 | 115 | return [x[0] | x[1] for x in zip(a, b)] 116 | 117 | def _xor_nums(self, a, b): 118 | if len(a) < 32: 119 | a = [0]* (32-len(a))+a 120 | if len(b) < 32: 121 | b = [0]* (32-len(b))+b 122 | 123 | return [x[0] ^ x[1] for x in zip(a, b)] 124 | 125 | def _and_nums(self, a, b): 126 | if len(a) < 32: 127 | a = [0]* (32-len(a))+a 128 | if len(b) < 32: 129 | b = [0]* (32-len(b))+b 130 | 131 | return [x[0] & x[1] for x in zip(a, b)] 132 | 133 | 134 | 135 | 136 | def _decode_harden_midop(self, enc, and_arr, shift): 137 | 138 | NEW = 0 139 | XOR = 1 140 | OK = 2 141 | work = [] 142 | for i in range(32): 143 | work.append((NEW,enc[i])) 144 | changed = True 145 | while changed: 146 | changed = False 147 | for i in range(32): 148 | status = work[i][0] 149 | data = work[i][1] 150 | if i >= 32-shift and status == NEW: 151 | work[i] = (OK,data) 152 | changed = True 153 | elif i < 32-shift and status == NEW: 154 | if and_arr[i] == 0: 155 | work[i] = (OK, data) 156 | changed = True 157 | else: 158 | work[i] = (XOR, data) 159 | changed = True 160 | elif status == XOR: 161 | i_other = i+shift 162 | if work[i_other][0] == OK: 163 | work[i] = (OK, data ^ work[i_other][1]) 164 | changed = True 165 | 166 | return [x[1] for x in work] 167 | 168 | 169 | def _harden(self, bits): 170 | bits = self._xor_nums(bits, bits[:-11]) 171 | bits = self._xor_nums(bits, self._and_nums(bits[7:] + [0] * 7 , self._to_bitarray(0x9d2c5680))) 172 | bits = self._xor_nums(bits, self._and_nums(bits[15:] + [0] * 15 , self._to_bitarray(0xefc60000))) 173 | bits = self._xor_nums(bits, bits[:-18]) 174 | return bits 175 | 176 | def _harden_inverse(self, bits): 177 | # inverse for: bits = _xor_nums(bits, bits[:-11]) 178 | bits = self._xor_nums(bits, bits[:-18]) 179 | # inverse for: bits = _xor_nums(bits, _and_nums(bits[15:] + [0] * 15 , _to_bitarray(0xefc60000))) 180 | bits = self._decode_harden_midop(bits, self._to_bitarray(0xefc60000), 15) 181 | # inverse for: bits = _xor_nums(bits, _and_nums(bits[7:] + [0] * 7 , _to_bitarray(0x9d2c5680))) 182 | bits = self._decode_harden_midop(bits, self._to_bitarray(0x9d2c5680), 7) 183 | # inverse for: bits = _xor_nums(bits, bits[:-11]) 184 | bits = self._xor_nums(bits, [0] * 11 + bits[:11]+[0] * 10) 185 | bits = self._xor_nums(bits, bits[11:21]) 186 | 187 | return bits 188 | 189 | 190 | def _regen(self): 191 | # C code translated from python sources 192 | N = 624 193 | M = 397 194 | MATRIX_A = 0x9908b0df 195 | LOWER_MASK = 0x7fffffff 196 | UPPER_MASK = 0x80000000 197 | mag01 = [self._to_bitarray(0), self._to_bitarray(MATRIX_A)] 198 | 199 | l_bits = self._to_bitarray(LOWER_MASK) 200 | u_bits = self._to_bitarray(UPPER_MASK) 201 | 202 | for kk in range(0,N-M): 203 | y = self._or_nums(self._and_nums( self.mt[kk], u_bits), self._and_nums(self.mt[kk+1],l_bits)) 204 | self.mt[kk] = self._xor_nums(self._xor_nums( self.mt[kk+M] , y[:-1]) , mag01[y[-1] & 1]) 205 | 206 | for kk in range(N-M-1, N-1): 207 | y = self._or_nums(self._and_nums( self.mt[kk], u_bits), self._and_nums(self.mt[kk+1],l_bits)) 208 | self.mt[kk] = self._xor_nums(self._xor_nums( self.mt[kk+(M-N)] , y[:-1]) , mag01[y[-1] & 1]) 209 | 210 | y = self._or_nums(self._and_nums( self.mt[N-1], u_bits), self._and_nums(self.mt[0],l_bits)) 211 | self.mt[N-1] = self._xor_nums(self._xor_nums( self.mt[M-1] , y[:-1]) , mag01[y[-1] & 1]) 212 | 213 | self.counter = 0 214 | 215 | 216 | if __name__ == "__main__": 217 | import random, time 218 | 219 | print("Testing random module cracker...") 220 | 221 | cracker = RandCrack() 222 | 223 | random.seed(time.time()) 224 | 225 | for i in range(624): 226 | cracker.submit(random.randint(0,4294967294)) 227 | 228 | print("Guessing next 32000 random bits success rate: {}%" 229 | .format(sum([random.getrandbits(32)==cracker.predict_getrandbits(32) for x in range(1000)])/10)) 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /CSAW Finals 2018/Disastrous Security Apparatus/images/challenge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/CSAW Finals 2018/Disastrous Security Apparatus/images/challenge.png -------------------------------------------------------------------------------- /CSAW Finals 2018/Disastrous Security Apparatus/images/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/CSAW Finals 2018/Disastrous Security Apparatus/images/warning.png -------------------------------------------------------------------------------- /CSAW Finals 2018/Disastrous Security Apparatus/main.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import hashlib 3 | import json 4 | import os 5 | import random 6 | import struct 7 | 8 | from cryptography.exceptions import InvalidSignature 9 | from cryptography.fernet import Fernet, InvalidToken 10 | from cryptography.hazmat.backends import default_backend 11 | from cryptography.hazmat.primitives import hashes 12 | from cryptography.hazmat.primitives.asymmetric.rsa import _modinv 13 | from cryptography.hazmat.primitives.serialization import load_pem_private_key 14 | 15 | from flask import Flask, abort, request 16 | 17 | 18 | app = Flask(__name__) 19 | 20 | 21 | with open("ctf.key", "rb") as f: 22 | pem_data = f.read() 23 | 24 | ctf_key = load_pem_private_key( 25 | pem_data, password=None, backend=default_backend() 26 | ) 27 | 28 | CSAW_FLAG = os.getenv("CSAW_FLAG") 29 | FERNET = Fernet(Fernet.generate_key()) 30 | 31 | 32 | @app.route("/capture", methods=["POST"]) 33 | def capture(): 34 | sig = binascii.unhexlify(request.form["signature"]) 35 | challenge = request.form["challenge"].encode("ascii") 36 | try: 37 | FERNET.decrypt(challenge) 38 | except InvalidToken: 39 | abort(400) 40 | try: 41 | ctf_key.public_key().verify(sig, challenge, hashes.SHA256()) 42 | return "flag{%s}" % CSAW_FLAG 43 | except InvalidSignature: 44 | abort(400) 45 | 46 | 47 | @app.route("/challenge") 48 | def challenge(): 49 | return FERNET.encrypt(b"challenged!") 50 | 51 | 52 | @app.route("/sign/") 53 | def signer(data): 54 | r, s = sign(ctf_key, data) 55 | return json.dumps({"r": r, "s": s}) 56 | 57 | 58 | @app.route("/forgotpass") 59 | def returnrand(): 60 | # Generate a random value for the reset URL so it isn't guessable 61 | random_value = binascii.hexlify(struct.pack(">Q", random.getrandbits(64))) 62 | return "https://innitech.local/resetpass/{}".format( 63 | random_value.decode("ascii") 64 | ) 65 | 66 | 67 | @app.route("/resetpass/ ") 68 | def resetpass(key): 69 | # TODO: Implement this later. Innitech doesn"t utilize users in this system 70 | # right now anyway. 71 | return "", 500 72 | 73 | 74 | @app.route("/public_key") 75 | def public_key(): 76 | pn = ctf_key.private_numbers() 77 | return json.dumps({ 78 | "g": pn.public_numbers.parameter_numbers.g, 79 | "q": pn.public_numbers.parameter_numbers.q, 80 | "p": pn.public_numbers.parameter_numbers.p, 81 | "y": pn.public_numbers.y 82 | }) 83 | 84 | 85 | @app.route("/") 86 | def main(): 87 | return "Welcome to Innitech. Good luck!" 88 | 89 | 90 | def sign(ctf_key, data): 91 | data = data.encode("ascii") 92 | pn = ctf_key.private_numbers() 93 | g = pn.public_numbers.parameter_numbers.g 94 | q = pn.public_numbers.parameter_numbers.q 95 | p = pn.public_numbers.parameter_numbers.p 96 | x = pn.x 97 | k = random.randrange(2, q) 98 | kinv = _modinv(k, q) 99 | r = pow(g, k, p) % q 100 | h = hashlib.sha1(data).digest() 101 | h = int.from_bytes(h, "big") 102 | s = kinv * (h + r * x) % q 103 | return (r, s) 104 | 105 | 106 | if __name__ == "__main__": 107 | app.run(host="0.0.0.0") 108 | -------------------------------------------------------------------------------- /CSAW Finals 2018/Disastrous Security Apparatus/solve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from cryptography.hazmat.primitives.asymmetric.rsa import _modinv 3 | from cryptography.hazmat.primitives import hashes 4 | from cryptography.hazmat.primitives.serialization import load_pem_private_key 5 | from cryptography.hazmat.backends import default_backend 6 | from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature 7 | from cracker import RandCrack 8 | 9 | import binascii 10 | import struct 11 | import requests 12 | import hashlib 13 | import sys 14 | 15 | base = 'http://localhost:5000' 16 | 17 | def get_numbers(cracker, data): 18 | l = [] 19 | for _ in range(312): 20 | sys.stdout.write('{}/312\r'.format(_)) 21 | l.append(requests.get(base+'/forgotpass').text) 22 | 23 | d = eval(requests.get(base+'/sign/'+data).text) 24 | r, s = d['r'], d['s'] 25 | pk = eval(requests.get(base+'/public_key').text) 26 | 27 | for i in range(312): 28 | n = l[i].split('/')[-1] 29 | n = binascii.unhexlify(n) 30 | n = struct.unpack('>Q', n)[0] 31 | r1 = n & (0xffffffff) 32 | r2 = (n & (0xffffffff00000000)) >> 32 33 | assert (n == ((r2 << 32) + r1)) 34 | cracker.submit(r1) 35 | cracker.submit(r2) 36 | k = cracker.predict_randrange(2, pk['q']) 37 | return pk, r, s, k 38 | 39 | def forge(data, pk, r, s, k): 40 | p, q, g, y = pk['p'], pk['q'], pk['g'], pk['y'] 41 | h1 = int(hashlib.sha1(data.encode()).hexdigest(), 16) 42 | h2 = int(hashlib.sha256(data.encode()).hexdigest(), 16) 43 | kinv = _modinv(k, q) 44 | 45 | x = ((s*k - h1) * _modinv(r, q)) % q 46 | assert y == pow(g,x,p), 'Error extracting private key' 47 | 48 | forge_s = kinv * (h2 + r * x) % q 49 | signature = encode_dss_signature(r, forge_s).hex() 50 | return signature, data 51 | 52 | def capture(signature, challenge): 53 | d = { 54 | 'signature': signature, 55 | 'challenge': challenge 56 | } 57 | return requests.post(base+'/capture', d).text 58 | 59 | def solve(): 60 | cracker = RandCrack() 61 | data = requests.get(base+'/challenge').text 62 | 63 | print('Cracking k...') 64 | pk, r, s, k = get_numbers(cracker, data) 65 | 66 | print('Forging signature...') 67 | signature, challenge = forge(data, pk, r, s, k) 68 | 69 | print('Capturing the flag...') 70 | flag = capture(signature, challenge) 71 | print(flag) 72 | 73 | if __name__ == '__main__': 74 | solve() 75 | -------------------------------------------------------------------------------- /CSAW Quals 2018/Algebra/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Algebra - 200 4 | 5 | ### Are you a real math wiz? 6 | 7 | nc misc.chal.csaw.io 9002 8 | 9 | ## Solution: 10 | 11 | Script: [algebra.py](./algebra.py) 12 | -------------------------------------------------------------------------------- /CSAW Quals 2018/Algebra/algebra.py: -------------------------------------------------------------------------------- 1 | import sympy 2 | from sympy.solvers import solve 3 | from pwn import remote 4 | 5 | def find(a): 6 | X = sympy.Symbol('X') 7 | left, right = a.split('=') 8 | eqn = left + '-(' + right + ')' 9 | try: 10 | sol = solve(eval(eqn), X)[0] 11 | except: 12 | sol = 0 13 | return str(eval(str(sol))) 14 | 15 | def recv(sh): 16 | return sh.recvline().decode('ascii')[:-1] 17 | 18 | def main(): 19 | sh = remote('misc.chal.csaw.io', 9002) 20 | print(recv(sh)); print(recv(sh)); print(recv(sh)); print(recv(sh)); print(recv(sh)); print(recv(sh)) 21 | i = 0 22 | while True: 23 | i += 1 24 | print(recv(sh)) 25 | data = recv(sh) 26 | print(sh.recv().decode('ascii')) 27 | print(data) 28 | if 'flag' in data: 29 | break 30 | r = find(data) 31 | print(r) 32 | sh.sendline(r) 33 | print('Solved: ' + str(i)) 34 | 35 | if __name__ == '__main__': 36 | main() -------------------------------------------------------------------------------- /CSAW Quals 2018/Take an L/README.md: -------------------------------------------------------------------------------- 1 | # Take an L - 200 2 | 3 | ### Fill the grid with L's but avoid the marked spot for the W 4 | 5 | 6 | nc misc.chal.csaw.io 9000 7 | 8 | The origin is at (0,0) on the top left 9 | 10 | 11 | [description.pdf](./description.pdf) 12 | 13 | ## Solution 14 | 15 | Script: [tiling.py](./tiling.py) 16 | -------------------------------------------------------------------------------- /CSAW Quals 2018/Take an L/description.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/CSAW Quals 2018/Take an L/description.pdf -------------------------------------------------------------------------------- /CSAW Quals 2018/Take an L/tiling.py: -------------------------------------------------------------------------------- 1 | 2 | from pwn import remote 3 | 4 | sol = '' 5 | 6 | def get_quad(n,o,p): 7 | if p[0]-o[0] < n/2 and p[1]-o[1] < n/2: 8 | return 1 9 | if p[0]-o[0] < n/2 and p[1]-o[1] >= n/2: 10 | return 2 11 | if p[0]-o[0] >= n/2 and p[1]-o[1] < n/2: 12 | return 3 13 | if p[0]-o[0] >= n/2 and p[1]-o[1] >= n/2: 14 | return 4 15 | 16 | def fill_tiles(n, o, p): 17 | assert (p[0]-o[0] < n and p[1]-o[1] < n) 18 | global sol 19 | if n == 2: 20 | blocks = [ 21 | (o[0], o[1]), 22 | (o[0]+1, o[1]), 23 | (o[0], o[1]+1), 24 | (o[0]+1, o[1]+1) 25 | ] 26 | l = [] 27 | for block in blocks: 28 | if not block == p: 29 | l.append(block) 30 | sol += '({},{}), ({},{}), ({},{})\n'.format(l[0][0],l[0][1], l[1][0],l[1][1], l[2][0],l[2][1]) 31 | return 32 | 33 | quad = get_quad(n,o,p) 34 | o1 = o 35 | o2 = (o[0],o[1]+n/2) 36 | o3 = (o[0]+n/2,o[1]) 37 | o4 = (o[0]+n/2,o[1]+n/2) 38 | 39 | p1 = (o[0]+n/2-1, o[1]+n/2-1) 40 | p2 = (o[0]+n/2-1, o[1]+n/2) 41 | p3 = (o[0]+n/2, o[1]+n/2-1) 42 | p4 = (o[0]+n/2, o[1]+n/2) 43 | 44 | if quad == 1: 45 | sol += '({},{}), ({},{}), ({},{})\n'.format(p2[0],p2[1], p3[0],p3[1], p4[0],p4[1]) 46 | fill_tiles(n/2, o1, p) 47 | fill_tiles(n/2, o2, p2) 48 | fill_tiles(n/2, o3, p3) 49 | fill_tiles(n/2, o4, p4) 50 | elif quad == 2: 51 | sol += '({},{}), ({},{}), ({},{})\n'.format(p1[0],p1[1], p3[0],p3[1], p4[0],p4[1]) 52 | fill_tiles(n/2, o1, p1) 53 | fill_tiles(n/2, o2, p) 54 | fill_tiles(n/2, o3, p3) 55 | fill_tiles(n/2, o4, p4) 56 | elif quad == 3: 57 | sol += '({},{}), ({},{}), ({},{})\n'.format(p1[0],p1[1], p2[0],p2[1], p4[0],p4[1]) 58 | fill_tiles(n/2, o1, p1) 59 | fill_tiles(n/2, o2, p2) 60 | fill_tiles(n/2, o3, p) 61 | fill_tiles(n/2, o4, p4) 62 | elif quad == 4: 63 | sol += '({},{}), ({},{}), ({},{})\n'.format(p1[0],p1[1] ,p2[0],p2[1], p3[0],p3[1]) 64 | fill_tiles(n/2, o1, p1) 65 | fill_tiles(n/2, o2, p2) 66 | fill_tiles(n/2, o3, p3) 67 | fill_tiles(n/2, o4, p) 68 | 69 | 70 | sh = remote('misc.chal.csaw.io', 9000) 71 | print(sh.recvuntil('dimensions ')) 72 | n = int(sh.recvline().split('x')[0]) 73 | print(sh.recvuntil(': ')) 74 | p = eval(sh.recvline()) 75 | 76 | sol = '' 77 | fill_tiles(n, (0,0), p) 78 | for tile in sol.split('\n')[:-1]: 79 | print(tile) 80 | sh.sendline(tile) 81 | print(sh.recv()) 82 | 83 | -------------------------------------------------------------------------------- /DCTF/Get Admin/README.md: -------------------------------------------------------------------------------- 1 | # Get Admin - 220 2 | 3 | 4 | This is a very unexpected gig for me. However, I'm busy with other projects so can you please give me a hand to test this. For free, of course. :-) 5 | 6 | Files: [https://dctf.def.camp/dctf-18-quals-81249812/get-admin.zip](https://dctf.def.camp/dctf-18-quals-81249812/get-admin.zip) 7 | 8 | Target: [https://admin.dctfq18.def.camp/](https://admin.dctfq18.def.camp/) 9 | 10 | 11 | ## Solution 12 | 13 | ### Overview 14 | The website is pretty basic, it lets you register an account with your name, password, email and lets you login. If your `id` is `1` (i.e. if you are `admin`), it prints the flag else says `Try Harder` (for all other users). There's an option to update your profile if needed and an option to logout. 15 | 16 | When we login to the website, it sets a cookie in our browser which is `AES-128-CBC` encrypted and contains our `id` (automatically set at the time of registration), `username`and `email` along with the `CRC-32 checksum`. Along with this encrypted data, it contains a `length` in the end which gives the length of the decrypted plaintext cookie. Odd. This was an unnecessary piece of information for this encryption/decryption scheme. We'll see later how this was used to exploit the decryption. 17 | 18 | ### Encryption of the Cookie 19 | Let's get to specifics. As soon as we login, the following cookie is set in `index.php`: 20 | ```php 21 | setcookie('user',encryptCookie([ 22 | 'id' => $userid, 23 | 'username' => $_POST['username'], 24 | 'email' => $row['email'], 25 | ]), time()+60*60*24*30); 26 | ``` 27 | 28 | Here's `encryptCookie()` function from `config.php` along with its helper functions: 29 | ```php 30 | function encryptCookie($arr) { 31 | $cookie = compress($arr); 32 | $arr['checksum'] = crc32($cookie); 33 | return encrypt(compress($arr), AES_KEY, AES_IV); 34 | } 35 | 36 | function compress($arr) { 37 | return implode('÷', array_map(function ($v, $k) { return $k.'¡'.$v; }, $arr, array_keys($arr) )); 38 | } 39 | 40 | function encrypt($plaintext, $key, $iv) { 41 | $length = strlen($plaintext); 42 | $ciphertext = openssl_encrypt($plaintext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv); 43 | return base64_encode($ciphertext) . sprintf('%06d', $length); 44 | } 45 | ``` 46 | 47 | `compress()` simply serializes the array into a string where key-value pairs are separated by a `÷` and inserts `¡` between each key and value. For example if `id = 1337, username = testac, email = fake@mail.com` then `compress()` returns `id¡1337÷username¡testac÷email¡fake@mail.com`. 48 | 49 | `encryptCookie()` takes `id`, `username` and `email` as inputs, calculates the `CRC-32` checksum of the serialized cookie, appends it to the cookie again. Now we get: `id¡1337÷username¡testac÷email¡fake@mail.com÷checksum¡2160329226` 50 | 51 | This string is then encrypted with `AES-128-CBC` and the length of the above string `70` (`¡` and `÷` are counted as length `2` each) padded with `0s` is appended to the resulting `base64` string. So this is our final cookie: 52 | `Rx5R751nNLFTDmwdj248byPKYFCReDmb8cTlK8m53X3TLG5WpUwYv+8zN0Ur2YVZ0q7giK51kNvFRjr36elyKiunyw6aPYR1BAE9dF6+7KU=000070` 53 | 54 | ### Decryption of the Cookie 55 | 56 | In `index.php`, if the cookie is already set but `_SESSION['userid']` is not, it tries to decrypt the cookie and if the `id` in it is greater than `0`, sets the `_SESSION['userid']` variable and redirects us to `admin.php`. Here's `decryptCookie()` and its helper function from `config.php`: 57 | ```php 58 | function decryptCookie($cypher) { 59 | return decompress(decrypt($cypher, AES_KEY, AES_IV)); 60 | } 61 | 62 | function decrypt($ciphertext, $key, $iv) { 63 | $length = intval(substr($ciphertext, -6, 6)); 64 | $ciphertext = substr($ciphertext, 0,-6); 65 | $output = openssl_decrypt(base64_decode($ciphertext), 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv); 66 | if($output == FALSE) { 67 | echo('Decryption error (0).'); 68 | die(); 69 | } 70 | return substr($output, 0, $length); 71 | } 72 | ``` 73 | 74 | `decryptCookie()` takes the encrypted cookie, separates the `length` from the ciphertext, decrypts the cipher and returns only the first `length` characters of the decrypted cookie. Mhmm. This is then passed to `decompressed()`: 75 | 76 | ```php 77 | function decompress($cookie) { 78 | if(preg_match('/[^\x00-\x7F]+\ *(?:[^\x00-\x7F]| )*/im',$cookie, $m) == 0) { 79 | echo('Decryption error (1).'); 80 | return false; 81 | } 82 | 83 | $t = explode("÷", $cookie); 84 | 85 | $arr = []; 86 | foreach($t as $el) { 87 | $el = explode("¡", $el); 88 | $arr[$el[0]] = $el[1]; 89 | } 90 | 91 | if(!isset($arr['checksum'])) { 92 | echo('Decryption error (2).'); 93 | return false; 94 | } 95 | 96 | $checksum = intval($arr['checksum']); 97 | unset($arr['checksum']); 98 | $cookie = compress($arr); 99 | if($checksum != crc32($cookie)) { 100 | echo('Decryption error (3).'); 101 | return false; 102 | } 103 | 104 | return $arr; 105 | } 106 | ``` 107 | 108 | The `decrypt()` function: 109 | - Checks if the decrypted cookie matches the regex 110 | - Constructs the array from its serialized form 111 | - Extracts the expected CRC-32 checksum 112 | - Computes the CRC-32 checksum of the remaining data 113 | - Checks if the two checksums match 114 | - Returns the array containing the user data 115 | 116 | This is then given back to `index.php` and it then redirects us to `admin.php`. There, if our `id == 1`, the flag is printed but as we are regular users, our `id > 1`. 117 | 118 | ### The Vulnerability 119 | 120 | During registration, the website put no restrictions on the characters entered in the `email` field. Think what happens if I put my email as `fake@mail.com÷id¡1`. 121 | My cookie would then be: `id¡1337÷username¡testac÷email¡fake@mail.com÷id¡1` along with its checksum. While decryption, due to the way `decompress()` is constructing the array, the array would be `id¡1÷username¡testac÷email¡fake@mail.com` as the latter `id` replaces the value of the former one. This is exactly what we want! 122 | 123 | But unfortunately the `CRC-32` checksum fails as the expected checksum (`checksum=732808468`) would be of the original data with 2 `id`s whereas the resulting one (`checksum=3870551952`) is of the data with only 1 `id`. Let's inject the checksum too then! 124 | 125 | Modifying our email to include the checksum of the data with `id=1`, our new cookie will be: `id¡1337÷username¡testac÷email¡fake@mail.com÷id¡1÷checksum¡3870551952` 126 | 127 | This will then be appended with its checksum giving: `id¡1337÷username¡testac÷email¡fake@mail.com÷id¡1÷checksum¡3870551952÷checksum¡2104704402` 128 | 129 | But now, our checksum gets overwritten by the new one just like we overwrote the previous value of `id`. This is where the `length` comes into picture! If we reduce the `length` only upto the first checksum (i.e. the first `77` characters), the `decompress()` function thinks that's the original checksum and decrypts the cookie without any errors! 130 | 131 | That is all we need to do to get `ADMIN ACCESS`! 132 | 133 | 1. Register with the following details: 134 | - username: `testac` 135 | - email: `fake@mail.com÷id¡1÷checksum¡3870551952` 136 | 2. Login and get your cookie (will result in `Decryption Error (3)`) 137 | 3. Logout, change the length in the cookie to `000077` and set the cookie 138 | 4. Navigate to admin.php 139 | 140 | And we have the flag! 141 | 142 |  143 | 144 | **`DCTF{4EF853DFC818AFEC39497CD1B91625F9E6E19D34D8E43E56722026F26A95F13E} `** 145 | -------------------------------------------------------------------------------- /DCTF/Get Admin/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/DCTF/Get Admin/flag.png -------------------------------------------------------------------------------- /DCTF/Message/README.md: -------------------------------------------------------------------------------- 1 | # Message - 50 2 | 3 | I just typed this secret [message](./message.txt) with my new encoding algorithm. 4 | 5 | ## Solution 6 | 7 | The message consists of lowercase letters, a few digits, periods, commas and spaces. It starts out with `wsxcvasdfghrfvbnhyt...`. This string is a concatenation of `wsxcv`, `asdfgh`, `rfvbnhyt`. Why did I split the message in this way? Well, these letters are adjacent to each other in the `qwerty-style` keyboard layout. 8 | 9 | We notice that these strings appear a lot of times in the message so looks like we need to substitute each one with something more meaningful. Now, if we trace out the motion of the letters on the keyboard, `wsxcv` looks like an `L`, `asdfgh` is probably `_` and `rfvbnhyt` looks like an `O`. Let's decrypt more and see what we get: 10 | ``` 11 | wsxcv -> L 12 | rfvbnhyt -> O 13 | mnbvcdrtghu -> R 14 | wsxcde -> ? 15 | zaqwdrtgb -> M 16 | 17 | wsx -> I 18 | nbvcxswefr -> ? 19 | iuyhnbv -> S 20 | wsxcvfr -> U 21 | zaqwdrtgb -> M 22 | 23 | asdfgh, qwerty, zxcvbn -> _ 24 | ``` 25 | 26 | Turns out our guess is right! Ignoring the underscores, the first two words are `LOR?M I?SUM` which is obviously the popular dummy text `LOREM IPSUM`! 27 | 28 | Replacing the strings with the letters in this manner, we obtain the final decrypted message: 29 | 30 | 31 | LOREM IPSUM IS SIMPLY DUMMY TEXT OF THE PRINTING AND TYPESETTING INDUSTRY 32 | LOREM IPSUM HAS BEEN THE INDUSTRY'S STANDARD DUMMY TEXT EVER SINCE THE 1500S, WHEN AN UNKNOWN PRINTER TOOK A GALLEY OF TYPE AND SCRAMBLED IT TO MAKE A TYPE SPECIMEN BOOK 33 | IT HAS SURVIVED NOT ONLY FIVE CENTURIES, BUT ALSO THE LEAP INTO ELECTRONIC TYPESETTING, REMAINING ESSENTIALLY UNCHANGED 34 | IT WAS POPULARISED IN THE 1960S WITH THE RELEASE OF LETRASET SHEETS CONTAINING LOREM IPSUM PASSAGES, AND MORE RECENTLY WITH DESKTOP PUBLISHING SOFTWARE LIKE ALDUS PAGEMAKER INCLUDING VERSIONS OF LOREM IPSUM 35 | DCTF{B66ECAAA90AD05DF5DAB33D71A8F70934408F3A5847A4C5C38DB75891B0F0E32}LOREM IPSUM IS SIMPLY DUMMY TEXT OF THE PRINTING AND TYPESETTING INDUSTRY 36 | LOREM IPSUM HAS BEEN THE INDUSTRY'S STANDARD DUMMY TEXT EVER SINCE THE 1500S, WHEN AN UNKNOWN PRINTER TOOK A GALLEY OF TYPE AND SCRAMBLED IT TO MAKE A TYPE SPECIMEN BOOK 37 | IT HAS SURVIVED NOT ONLY FIVE CENTURIES, BUT ALSO THE LEAP INTO ELECTRONIC TYPESETTING, REMAINING ESSENTIALLY UNCHANGED 38 | IT WAS POPULARISED IN THE 1960S WITH THE RELEASE OF LETRASET SHEETS CONTAINING LOREM IPSUM PASSAGES, AND MORE RECENTLY WITH DESKTOP PUBLISHING SOFTWARE LIKE ALDUS PAGEMAKER INCLUDING VERSIONS OF LOREM IPSUM. 39 |40 | 41 | And we have our flag: 42 | **`DCTF{B66ECAAA90AD05DF5DAB33D71A8F70934408F3A5847A4C5C38DB75891B0F0E32}`** 43 | -------------------------------------------------------------------------------- /DCTF/Message/message.txt: -------------------------------------------------------------------------------- 1 | wsxcvasdfghrfvbnhytqwertymnbvcdrtghuzxcvbnwsxcdeasdfghzaqwdrtgbzxcvbn qwertywsxqwertynbvcxswefrqwertyiuyhnbvqwertywsxcvfrasdfghzaqwdrtgbzxcvbn qwertywsxasdfghiuyhnbvasdfgh zxcvbnytrfvcxqwertywsxasdfghzaqwdrtgbqwertymnbvccdertgzxcvbnedcvbasdfghefvtzxcvbn asdfghwsxcfezxcvbnedcvbgtasdfghzaqwdrtgbqwertyxsweftynhzxcvbnjmyizxcvbn zxcvbnrtyuihnzxcvbnwsxcdeasdfghrgnygcqwertyrtyuihnasdfgh qwertyqazxcdewzxcvbnredcfzxcvbn zxcvbnwertyfvzxcvbnrfvgyhnasdfghwsxcdeqwerty qwertynbvcxswefrzxcvbnmnbvcdrtghuzxcvbnrfvqwertyxsweftgbqwertyrtyuihnqwertywsxasdfghxsweftgbzxcvbncvgredasdfgh asdfghgrdxcvbzxcvbnxsweftbgasdfghwsxcfeqwerty asdfghwertyfvqwertyefvtqwertynbvcxswefrqwertyedcvrfzxcvbnytrfvcxzxcvbntgbnhyasdfghwertyfvasdfghwertyfvzxcvbnwsxqwertyzaqwdvfrzxcvbncvgredqwerty asdfghedczxcvbnxsweftbgasdfghyhnmkuzxcvbnedcvbgtqwertyiuyhnbvqwertyrtyuihnasdfghmnbvcdrtghuqwertyjmyiasdfgh. 2 | zxcvbn asdfghrfvbnqwertywsxcvfreasdfghmnbvcdrtghuqwertywsxcdeasdfghzaqwdrtgbasdfgh asdfghwsxqwertynbvcxswerfqwertyiuyhnbvzxcvbnedcvbgtzxcvbnxsweftyhnqwerty qwertyrfvgyhnasdfghzsefvcxqwertyytrfvcxasdfgh qwertyrfvbhgasdfghedcvrfasdfghtgbnhyasdfghxsweftgbasdfgh asdfghrtyuihnqwertyedcftgbasdfghtgbnhyasdfgh zxcvbnrfvasdfghzaqwdvfrasdfghwsxcfezxcvbnedcvbgtqwertyytrfvcxzxcvbnrtyuihnasdfghmnbvcdrtghuzxcvbnefvtzxcvbn'asdfghiuyhnbvzxcvbn qwertyiuyhnbvzxcvbnrtyuihnqwertyzsefvcxasdfghzaqwdvfrqwertyyhnmkuqwertyzsefvcxzxcvbnmnbvcdrtghuqwertyedcvgrzxcvbn zxcvbnwsxcfeqwertyedcvbgtasdfghxsweftynhasdfghxsweftyhnzxcvbnefvtqwerty zxcvbnrtyuihnzxcvbntgbnhyqwertywdvtdzasdfghwertyfvzxcvbn asdfghtgbnhyqwertywdcftasdfghedcvrfasdfghmnbvcdrtghuqwerty zxcvbniuyhnbvzxcvbnrfvqwertyxsweftgbzxcvbnewsxczxcvbnwsxcdezxcvbn qwertywertyfvqwertyedcftgbzxcvbnedcvrfzxcvbn asdfgh1qwerty5zxcvbn0asdfgh0qwertyiuyhnbvzxcvbn,qwerty zxcvbnefvgywdcftzxcvbnedcftgbasdfghwsxcdeqwertyxsweftgbzxcvbn zxcvbngrdxcvbasdfghzaqwdvfrqwerty asdfghwsxcvfrqwertyzaqwdvfrasdfghedcfbyasdfghxsweftbgzxcvbnwsxcvfreqwertyefvgywdcftzxcvbnxsweftbgasdfgh asdfghnbvcxswerfzxcvbnmnbvcdrtghuqwertyedczxcvbnxsweftbgqwertywertyfvzxcvbnedcvrfzxcvbnmnbvcdrtghuqwerty qwertyrtyuihnqwertyqazxcdewzxcvbnwsxcvfreqwertyedcfbyasdfgh qwertygrdxcvbasdfgh zxcvbncvgredzxcvbnxcvbgrdasdfghrfvbnasdfghrfvbnasdfghwsxcdeqwertyefvtqwerty qwertyqazxcdewqwertytrfvgzxcvbn zxcvbnwertyfvzxcvbnefvtqwertymnbvccdertgqwertyedcvrfasdfgh zxcvbngrdxcvbzxcvbnzaqwdvfrqwertyyhnmkuqwerty qwertyiuyhnbvqwertyredcvasdfghmnbvcdrtghuzxcvbnzsefvcxqwertyxsweftynhzxcvbnrfvbhgzxcvbnwsxcvasdfghwsxcdezxcvbnedcvgrasdfgh qwertyrfvqwertyrtyuihnzxcvbn qwertyrtyuihnqwertyqazxcdewzxcvbn asdfghxsweftynhqwertyzsefvcxzxcvbnqazsceasdfghedcvrfqwerty asdfghxcvbgrdasdfgh asdfghrtyuihnzxcvbnjmyizxcvbnmnbvccdertgqwertywsxcdeqwerty asdfghiuyhnbvasdfghnbvcxswerfasdfghwsxcdezxcvbnewsxcqwertyedcasdfghxsweftynhasdfghedcvrfqwertyxsweftbgqwerty qwertywsxcfdqwertywsxcvfreasdfghwsxcvfreqwertyedcfbyasdfgh. 3 | asdfgh asdfghedczxcvbnrtyuihnasdfgh asdfghwsxdrfvzxcvbnxcvbgrdzxcvbniuyhnbvqwerty asdfghiuyhnbvqwertywsxcvfrasdfghmnbvcdrtghuqwertyefvgyasdfghwsxqwertyefvgyqwertytgbnhyqwertywsxcfezxcvbn qwertyzaqwdvfrasdfghrfvbnhytasdfghrtyuihnqwerty asdfghwsxcvfreqwertyzaqwdvfrzxcvbnrfvbnzxcvbnefvtzxcvbn qwertyewsxdzxcvbnrfvzxcvbnwdcftzxcvbntgbnhyqwerty zxcvbnewsxcqwertytgbnhyqwertyzaqwdvfrasdfghrtyuihnasdfghedcvbgtqwertymnbvcdrtghuzxcvbnwsxqwertyedcvrfqwertyiuyhnbvzxcvbn,qwerty zxcvbnrfvbhgasdfghedcvbgtqwertyrtyuihnasdfgh zxcvbnxcvbgrdasdfghwsxcvzxcvbnytrfvcxzxcvbnqazxcdewzxcvbn zxcvbnrtyuihnqwertyrfvgyhnqwertywsxcdeasdfgh qwertyrfvbnzxcvbnedcvrfzxcvbngrdxcvbqwertymnbvccdertgzxcvbn qwertywsxzxcvbnzaqwdvfrasdfghwertyfvqwertywsxcvfreasdfgh asdfghtgbnhyasdfghrfvbnqwertyedcvrfzxcvbntrfvbqwertyrtyuihnzxcvbnmnbvcdrtghuzxcvbnrfvbnhytqwertyxsweftgbzxcvbnwsxqwertyredcvzxcvbn asdfghrtyuihnqwertyefvtqwertynbvcxswerfasdfghwsxcdeqwertyiuyhnbvzxcvbnwsxcdezxcvbnwertyfvqwertyrtyuihnasdfghwsxzxcvbnxsweftgbqwertycvgredqwerty,zxcvbn qwertymnbvcdrtghuzxcvbnedcvrfqwertyxsweftyhnasdfghxcvbgrdzxcvbnedcqwertyxsweftgbasdfghedcqwertyxsweftbgasdfghcvrgedqwerty qwertyedcvrfzxcvbniuyhnbvqwertyiuyhnbvasdfghedcvrfqwertyxsweftbgasdfghrtyuihnzxcvbnrfvzxcvbnzsefvcxqwertywsxcvasdfghrfvbnzxcvbnefvtzxcvbn zxcvbnedcvbgtqwertyxsweftbgqwertytrfvbqwertyedcftgbasdfghxcvbgrdqwertyxsweftbgzxcvbncvrgedzxcvbnwsxcdeqwertyyhnmkuasdfgh. 4 | asdfgh qwertywsxasdfghwertyfvasdfgh qwertyefvgywdcftasdfghxcvbgrdasdfghiuyhnbvzxcvbn qwertymnbvccdertgzxcvbnwsxcvfreasdfghnbvcxswefrqwertyedcvbgtasdfghrfvbnqwertyzsefvcxzxcvbnmnbvcdrtghuzxcvbnwsxqwertyytrfvcxzxcvbnwsxcdeqwertywsxcfezxcvbn qwertyrfvzxcvbnzaqwdvfrqwerty asdfghrtyuihnzxcvbnrfvgyhnasdfghwsxcdeasdfgh zxcvbn1qwerty9qwerty6zxcvbn0asdfghytrfvcxqwerty asdfghefvgywdcftqwertyrfvasdfghrtyuihnqwertywsxdrfvzxcvbn asdfghwertyfvzxcvbnedcftgbqwertyedcvrfasdfgh asdfghmnbvcdrtghuzxcvbnwsxcdeasdfghrfvbnasdfghedcvrfzxcvbngrdxcvbqwertyytrfvcxqwertyedcvrfasdfgh asdfghwsxcvfrezxcvbnewsxdqwerty qwertyrfvbnzxcvbntgbnhyasdfghwertyfvasdfghmnbvcdrtghuqwertygrdxcvbasdfghytrfvcxqwertyedcvrfqwertyrtyuihnqwerty zxcvbniuyhnbvzxcvbnrfvgyhnqwertytgbnhyasdfghtgbnhyasdfghrtyuihnzxcvbnytrfvcxqwerty zxcvbnredcvqwertyrfvbnhytasdfghxsweftbgzxcvbnrtyuihnasdfghxcvbgrdqwertyedczxcvbnxsweftgbqwertyedcasdfghxsweftbgasdfghcvrgedqwerty asdfghrfvbnasdfghqazxcdewqwertymnbvcdrtghuqwertywsxcdeasdfghxsweftyhnzxcvbn qwertyedczxcvbnnbvcxswerfasdfghiuyhnbvzxcvbnedcvbgtasdfghxsweftynhzxcvbn zxcvbnnbvcxswerfasdfghxcvbgrdzxcvbniuyhnbvqwertyytrfvcxqwertygrdxcvbqwertycvrgedqwertyedcvrfzxcvbniuyhnbvqwerty,asdfgh zxcvbnxcvbgrdqwertyzaqwdvfrzxcvbnwsxcfeqwerty asdfghxsweftyhnzxcvbnrfvbnhytasdfghmnbvcdrtghuasdfghtgbnhyasdfgh qwertymnbvcdrtghuqwertywsxcdezxcvbntrfvbqwertyedcvrfzxcvbnxsweftbgqwertywertyfvqwertyedcvbzxcvbnefvtqwerty qwertyefvgywdcftasdfghrfvqwertyrtyuihnasdfghedcftgbasdfgh zxcvbnyhnmkuasdfghtgbnhyqwertyytrfvcxasdfghqazsceasdfghrtyuihnasdfghrfvbnhytzxcvbnnbvcxswerfzxcvbn qwertynbvcxswefrqwertyedcvbgtasdfghrfvbhgzxcvbnedcvbqwertyrfvzxcvbniuyhnbvqwertywsxdrfvasdfghwsxzxcvbnxsweftbgasdfghcvgredasdfgh zxcvbniuyhnbvasdfghwsxcvfrezxcvbntrfvgasdfghrtyuihnzxcvbnefvgywdcftasdfghgrdxcvbzxcvbnmnbvcdrtghuasdfghedcvrfqwerty zxcvbnrfvbnqwertywsxqwertywsxdvrqwertywsxcdeqwerty qwertyxcvbgrdqwertyrfvbnqwertyedcvgrqwertywsxcvfrzxcvbnytrfvcxqwerty qwertymnbvccdertgzxcvbnxcvbgrdzxcvbncvgredqwertywsxcdeasdfghxsweftynhqwertyzsefvcxqwertywsxdvrasdfghedcvrfqwertymnbvcdrtghuasdfgh qwertywsxasdfghzaqwdvfrqwertyewsxczxcvbnrfvbnzxcvbnedcvbgtqwertyedcvgrzxcvbnwsxzxcvbnxsweftbgasdfghredcvgasdfgh zxcvbnwdcftasdfghtgbnhyzxcvbnmnbvcdrtghuqwertyiuyhnbvzxcvbnedcqwertywsxcvfreasdfghzaqwdvfrqwertyiuyhnbvzxcvbn zxcvbnwsxcvfreqwertyewsxdzxcvbn qwertyrfvbnqwertyqazxcdewasdfghmnbvcdrtghuzxcvbnwsxcdezxcvbnxsweftynhasdfgh zxcvbnrfvasdfghnbvcxswerfzxcvbniuyhnbvasdfghedcvbgtqwertyxsweftyhnqwerty. 5 | qwerty qwertywsxcfeasdfghtrfvbasdfghrtyuihnzxcvbnredcfqwerty{zxcvbnrfvbhgzxcvbn6zxcvbn6asdfghedcvrfzxcvbnredcvasdfghzsefvcxasdfghxcvbgrdasdfghgrdxcvbzxcvbn9qwerty0zxcvbnzsefvcxzxcvbnedcvgrasdfgh0asdfgh5qwertyyhnmkuasdfghredcfasdfgh5zxcvbnwsxcfeqwertyzsefvcxasdfghwsxcfdqwerty3qwerty3qwertyedcvgrqwerty7zxcvbn1qwertygrdxcvbqwerty8asdfghtrfvgzxcvbn7zxcvbn0qwerty9qwerty3qwerty4zxcvbn4zxcvbn0qwerty8zxcvbnredcfzxcvbn3zxcvbnzsefvcxzxcvbn5zxcvbn8qwerty4asdfgh7qwertyzsefvcxasdfgh4asdfghewsxczxcvbn5zxcvbntrfvbzxcvbn3asdfgh8zxcvbnedcvgrqwertyqazxdsasdfgh7zxcvbn5qwerty8asdfgh9asdfgh1qwertyrfvbhgasdfgh0zxcvbntrfvgasdfgh0qwertywsxcdezxcvbn3qwerty2zxcvbn}qwertywsxcvqwertyrfvbnhytzxcvbnmnbvcdrtghuasdfghtgbnhyqwertyxsweftyhnqwerty qwertyrfvasdfghmnbvccdertgqwertyiuyhnbvzxcvbnwsxcvfrzxcvbnxsweftyhnzxcvbn zxcvbnedczxcvbniuyhnbvqwerty asdfghytrfvcxasdfghwsxqwertyzaqwdrtgbqwertynbvcxswefrzxcvbnwsxcvqwertyjmyizxcvbn zxcvbnwsxcfeqwertyedcvbgtqwertyxsweftyhnzxcvbnxsweftyhnasdfghefvtzxcvbn asdfghrtyuihnqwertyedcvrfasdfghwdvtdzzxcvbnwertyfvzxcvbn asdfghrfvbnhytqwertytrfvgzxcvbn qwertywertyfvqwertywsxdrfvzxcvbnwsxcdeasdfgh asdfghnbvcxswefrasdfghmnbvcdrtghuzxcvbnwsxasdfghxsweftgbzxcvbnrtyuihnzxcvbnrfvzxcvbnxsweftgbqwertycvgredqwerty zxcvbngrdxcvbzxcvbnxsweftbgasdfghedcvgrasdfgh zxcvbnrtyuihnasdfghjmyizxcvbnnbvcxswerfasdfghtgbnhyqwertyiuyhnbvqwertytgbnhyasdfghwertyfvzxcvbnrtyuihnzxcvbnwsxzxcvbnxsweftgbasdfghcvrgedqwerty asdfghedcqwertyxsweftgbasdfghwsxcfezxcvbnwsxcvfrzxcvbniuyhnbvqwertyrtyuihnasdfghmnbvcdrtghuzxcvbnefvtzxcvbn. 6 | zxcvbn qwertyrfvbnzxcvbnwsxcvfreasdfghmnbvcdrtghuzxcvbnedcvrfasdfghzaqwdrtgbasdfgh qwertyedcasdfghmnbvccdertgqwertyytrfvcxasdfghwsxcvfrasdfghzaqwdrtgbasdfgh asdfghrfvgyhnzxcvbnxcvbgrdasdfghiuyhnbvzxcvbn zxcvbnqazxdsqwertyedcvrfzxcvbnedcvrfzxcvbnxsweftgbzxcvbn zxcvbnrtyuihnzxcvbnwsxdrfvqwertyedcvrfasdfgh zxcvbnedcqwertyzaqwdvfrqwertyyhnmkuasdfghedcvbgtasdfghytrfvcxasdfghwertyfvzxcvbnmnbvcdrtghuzxcvbnjmyiasdfgh'asdfghytrfvcxasdfgh qwertyytrfvcxasdfghrtyuihnzxcvbnxcvbgrdasdfghzaqwdvfrzxcvbnedcvgrzxcvbnzsefvcxasdfghmnbvcdrtghuasdfghyhnmkuqwerty qwertywsxcfezxcvbnedcvbgtzxcvbnxsweftyhnasdfghxsweftyhnasdfghefvtqwerty asdfghrtyuihnzxcvbnedcvrfzxcvbnwdvtdzzxcvbnrtyuihnqwerty asdfghwsxcdeqwertywdcftzxcvbnwsxcdeqwertymnbvcdrtghuzxcvbn zxcvbniuyhnbvzxcvbnrfvzxcvbnzaqwdvfrasdfghtrfvbasdfghedcvrfzxcvbn zxcvbnwertyfvasdfghrfvgyhnzxcvbntgbnhyzxcvbn asdfgh1zxcvbn5asdfgh0zxcvbn0qwertyiuyhnbvasdfgh,asdfgh asdfghefvgywdcftasdfghedcftgbqwertyedcvrfasdfghxsweftbgqwerty asdfghzsefvcxzxcvbnxsweftbgqwerty zxcvbnedcvbgtasdfghxsweftbgqwertyedcfbyasdfghzaqwdvfrzxcvbnwsxcvfreasdfghefvgywdcftqwertyxsweftbgqwerty qwertynbvcxswerfasdfghmnbvcdrtghuqwertyedcasdfghxsweftgbzxcvbnrtyuihnqwertytgbnhyzxcvbnmnbvcdrtghuasdfgh zxcvbnwertyfvzxcvbnqazxcdewqwertyrfvbnhytasdfghwsxdvrasdfgh zxcvbnxcvbgrdzxcvbn zxcvbnredcvgzxcvbnzsefvcxzxcvbnrfvbnzxcvbnrfvbnqwertyedcvrfzxcvbnjmyiasdfgh zxcvbnrfvbnhytqwertyewsxdzxcvbn zxcvbnwertyfvasdfghjmyiqwertynbvcxswerfzxcvbntgbnhyqwerty asdfghxcvbgrdasdfghxsweftbgasdfghedcvgrqwerty asdfghiuyhnbvqwertytrfvbqwertymnbvcdrtghuzxcvbngrdxcvbqwertyxsweftyhnasdfghrfvbhgzxcvbnedcvbzxcvbnwsxcdeqwertyedcvgrasdfgh zxcvbnrfvzxcvbnrtyuihnqwerty zxcvbnwertyfvqwertyqazxcdewzxcvbn asdfghxsweftynhqwertygrdxcvbqwertyqazsceqwertytgbnhyasdfgh asdfghgrdxcvbzxcvbn zxcvbnrtyuihnasdfghefvtzxcvbnnbvcxswerfzxcvbntgbnhyzxcvbn asdfghiuyhnbvqwertynbvcxswefrzxcvbnedcvrfzxcvbnredcvzxcvbnrfvqwertyxsweftynhasdfghtgbnhyqwertyzaqwdvfrasdfgh asdfghrfvbhgzxcvbnwsxcvfreqwertyrfvbnhytqwertywsxdvrasdfgh. 7 | asdfgh zxcvbnedcasdfghwertyfvasdfgh qwertyedcftgbzxcvbnxcvbgrdasdfghiuyhnbvqwerty qwertyiuyhnbvasdfghwsxcvfrzxcvbnmnbvcdrtghuasdfghefvgyzxcvbnwsxasdfghwdcftqwertywsxcdezxcvbnwsxcfeasdfgh zxcvbnzaqwdvfrzxcvbnqazxcdewqwertywertyfvqwerty qwertyqazxcdewzxcvbnxsweftbgqwertywsxcvqwertyefvtqwerty asdfghtrfvgzxcvbnwsxasdfghwdcftqwertywsxcdezxcvbn asdfghtrfvbasdfghedcvrfasdfghxsweftgbasdfghrtyuihnqwertyedcvbgtasdfghmnbvcdrtghuasdfghwsxqwertytgbnhyqwertyiuyhnbvzxcvbn,qwerty zxcvbnwsxcfdzxcvbnedcvbgtqwertyrtyuihnqwerty zxcvbnxcvbgrdzxcvbnrfvbnasdfghytrfvcxasdfghwsxcvfreqwerty zxcvbnrtyuihnasdfghwsxdrfvqwertywsxcdeqwerty asdfghwsxcvzxcvbntgbnhyzxcvbnxcvbgrdzxcvbnnbvcxswefrzxcvbn asdfghwsxqwertyxsweftgbqwertywertyfvasdfghwsxcvfreqwerty asdfghedcvrfqwertywsxcvzxcvbntgbnhyasdfghredcvqwertywertyfvzxcvbnmnbvcdrtghuasdfghwsxcvfreasdfghxsweftbgzxcvbnedcasdfghewsxcqwerty zxcvbnrtyuihnqwertyjmyiqwertynbvcxswerfqwertytgbnhyqwertyytrfvcxzxcvbntgbnhyzxcvbnrtyuihnzxcvbnwertyfvzxcvbnedczxcvbnxsweftgbasdfghredcvgzxcvbn,qwerty asdfghmnbvcdrtghuqwertytgbnhyzxcvbnxsweftyhnzxcvbnzsefvcxasdfghrfvzxcvbnxsweftbgasdfghedcqwertyxsweftbgasdfghredcvgzxcvbn asdfghwsxcdeqwertyiuyhnbvqwertyytrfvcxqwertywsxcdezxcvbnxsweftgbzxcvbnrtyuihnasdfghedcqwertyxcvbgrdasdfghedcvbqwertyrfvbnqwertyefvtqwerty zxcvbnedcvbgtasdfghxsweftbgzxcvbntrfvbasdfghrfvgyhnqwertygrdxcvbasdfghxsweftgbasdfghcvgredasdfghwsxcdezxcvbnwsxcfezxcvbn. 8 | qwerty qwertyrfvasdfghrtyuihnzxcvbn qwertyefvgywdcftzxcvbngrdxcvbzxcvbniuyhnbvasdfgh qwertynbvcxswerfqwertywsxcvfrezxcvbnmnbvccdertgasdfghedcvbgtzxcvbnrfvbnasdfghxcvbgrdzxcvbnmnbvcdrtghuasdfghwsxzxcvbniuyhnbvzxcvbnedcvrfzxcvbnedcvgrzxcvbn zxcvbnwsxqwertyzaqwdvfrqwerty qwertyrtyuihnqwertyrfvgyhnqwertywsxcdezxcvbn zxcvbn1qwerty9asdfgh6zxcvbn0zxcvbniuyhnbvqwerty asdfghefvgywdcftasdfghwsxzxcvbnrtyuihnzxcvbnwsxdrfvasdfgh asdfghwertyfvasdfghrfvgyhnzxcvbnwsxcdezxcvbn qwertymnbvcdrtghuzxcvbnedcvrfzxcvbnwsxcvzxcvbntgbnhyasdfghgrdxcvbasdfghiuyhnbvzxcvbnedcvrfqwerty qwertyqazxcdewzxcvbnewsxdasdfgh asdfghedcvbzxcvbnedcvrfzxcvbnwertyfvzxcvbnmnbvcdrtghuzxcvbnzsefvcxqwertyiuyhnbvasdfghtgbnhyzxcvbnrtyuihnasdfgh qwertyiuyhnbvzxcvbnrfvgyhnqwertytgbnhyqwertytgbnhyzxcvbnrtyuihnasdfghiuyhnbvzxcvbn asdfghtrfvbqwertyrfvbnhytqwertyzaqwdvfrqwertyrtyuihnzxcvbngrdxcvbzxcvbnwsxqwertyxsweftgbzxcvbnwsxqwertyxsweftgbzxcvbncvgredqwerty zxcvbnedcvbzxcvbnrfvbnhytzxcvbnmnbvcdrtghuasdfghtgbnhyasdfghxsweftyhnqwerty qwertywsxzxcvbnnbvcxswefrasdfghytrfvcxasdfghedcvbgtqwertyxsweftyhnasdfgh qwertynbvcxswerfqwertyzsefvcxasdfghiuyhnbvasdfghiuyhnbvasdfghxcvbgrdasdfghredcvgasdfghtgbnhyzxcvbnytrfvcxasdfgh,asdfgh zxcvbnzsefvcxqwertyxsweftgbqwertyedcvgrzxcvbn qwertyzaqwdrtgbqwertywsxcvfrezxcvbnmnbvcdrtghuzxcvbnwsxcdezxcvbn zxcvbnmnbvcdrtghuasdfghedcvrfasdfghredcvqwertytgbnhyqwertyxsweftbgasdfghrtyuihnqwertyedcvbqwertyefvtzxcvbn qwertyefvgywdcftzxcvbnedczxcvbnwertyfvqwertywsxdrfvasdfgh zxcvbnwsxcfeasdfghedcvrfqwertyiuyhnbvasdfghedcfbyasdfghwertyfvqwertywsxcvfreasdfghnbvcxswerfqwerty asdfghnbvcxswefrqwertyedcvbgtzxcvbnwsxcfdqwertywsxcvqwertyedcqwertyytrfvcxasdfghwsxdrfvasdfghedczxcvbnxsweftgbzxcvbncvrgedzxcvbn asdfghiuyhnbvasdfghrfvbnhytasdfghtrfvgqwertyrtyuihnasdfghefvgywdcftasdfghzsefvcxasdfghmnbvcdrtghuasdfghtgbnhyzxcvbn asdfghrfvbnzxcvbnwsxqwertywsxdvrqwertywsxcdeasdfgh zxcvbnxcvbgrdasdfghwsxcvzxcvbnyhnmkuqwertyedcvbgtzxcvbniuyhnbvasdfgh zxcvbnnbvcxswerfasdfghzsefvcxqwertycvgredasdfghedcvrfasdfghxsweftynhqwertyxcvbgrdasdfghwsxdvrasdfghedcvrfasdfghmnbvcdrtghuqwerty asdfghrfvasdfghxsweftgbqwertyewsxczxcvbnwsxcvasdfghwsxcvfrqwertyyhnmkuzxcvbnwsxzxcvbnxsweftbgqwertycvgredqwerty asdfghefvgyzxcvbnedcvrfzxcvbnmnbvcdrtghuqwertyiuyhnbvqwertywsxzxcvbnwsxcvfrezxcvbnxsweftbgzxcvbniuyhnbvqwerty asdfghwsxcvfreqwertyredcfqwerty asdfghedcvbqwertyqazxcdewasdfghmnbvcdrtghuasdfghwsxcdeqwertyxsweftynhqwerty qwertyedcqwertymnbvccdertgzxcvbniuyhnbvasdfghwsxcvfrqwertyzaqwdrtgbqwerty. 9 | asdfgh -------------------------------------------------------------------------------- /Google CTF 2018/JS Safe 2.0/README.md: -------------------------------------------------------------------------------- 1 | # JS Safe 2.0 - 121 2 | 3 | Here's the challenge task from the web category: 4 | 5 |  6 | 7 | You stumbled upon someone's "JS Safe" on the web. It's a simple HTML file that can store secrets in the browser's localStorage. This means that you won't be able to extract any secret from it (the secrets are on the computer of the owner), but it looks like it was hand-crafted to work only with the password of the owner... 8 | 9 | [js_safe_2.html](./js_safe_2.html) 10 | 11 | 12 | 13 | ## The Overview 14 | 15 | Downloading the html file and viewing it locally gives us a cool looking cube (intended to be the JS Safe I presume) and an input box for the password. 16 | 17 |  18 | 19 | Whenever you type the password and hit enter (return), the `open_safe()` function gets triggered. 20 | 21 | ```js 22 | password = /^CTF{([0-9a-zA-Z_@!?-]+)}$/.exec(keyhole.value); 23 | if (!password || !x(password[1])) return document.body.className = 'denied'; 24 | ``` 25 | 26 | The above two statements from `open_safe()` suggest the password consists of alphanumeric characters and a few other symbols and must be enclosed by `CTF{}`. That's the first condition in the `if` statement. The second condition takes us to the function `x()`. After using a JS Beautifier, here's the function: 27 | ```js 28 | function x(х) { 29 | ord = Function.prototype.call.bind(''.charCodeAt); 30 | chr = String.fromCharCode; 31 | str = String; 32 | 33 | function h(s) { 34 | for (i = 0; i != s.length; i++) { 35 | a = ((typeof a == 'undefined' ? 1 : a) + ord(str(s[i]))) % 65521; 36 | b = ((typeof b == 'undefined' ? 0 : b) + a) % 65521 37 | } 38 | return chr(b >> 8) + chr(b & 0xFF) + chr(a >> 8) + chr(a & 0xFF) 39 | } 40 | 41 | function c(a, b, c) { 42 | for (i = 0; i != a.length; i++) c = (c || '') + chr(ord(str(a[i])) ^ ord(str(b[i % b.length]))); 43 | return c 44 | } 45 | for (a = 0; a != 1000; a++) debugger; 46 | x = h(str(x)); 47 | source = /Ӈ#7ùª9¨M¤À.áÔ¥6¦¨¹.ÿÓÂ.Ö£JºÓ¹WþÊmãÖÚG¤ ¢dÈ9&òªћ#³1᧨/; 48 | source.toString = function() { 49 | return c(source, x) 50 | }; 51 | try { 52 | console.log('debug', source); 53 | with(source) return eval('eval(c(source,x))') 54 | } catch (e) {} 55 | } 56 | ``` 57 | 58 | Notice how both, the name of the function and the parameter it takes, both, *look* like `x`. Sublime/VSCode actually helped me here to realize that the parameter `х` isn't the `x` from our alphabet. Instead, it is a [Cyrillic](https://en.wiktionary.org/wiki/%D1%85) alphabet which only looks like it. For now let's just keep in mind that we're dealing with two different `x`'s here. 59 | 60 | The next three lines are just defining the usual `ord`, `chr` and `str` functions. The function `h()` seems to some sort of a hashing algorithm which returns 4 characters no matter the size of the input. 61 | 62 | Next is the function `c()` which is essentially taking a string `a`, a key `b`, performing a repeated xor on the two and returning the resulting string. 63 | 64 | Then is the `for` loop with the `debugger` statements which we can can ignore for now. 65 | 66 | Then comes the statement `x = h(str(x))`. Here the `x` is the entire function itself in its raw (unmodified) form which is given to `h()` as a string and the resulting string (which has a length of 4) is being stored in `x` again. 67 | We need the original value of `x` as we modified the code (by prettifying it). Let's run the original html file again with debugging enabled. 68 | 69 | ## The Solution 70 | 71 | Open up developer tools in the browser and enter any password of the format we extracted above (like `CTF{qwerty}`). 72 | 73 |  74 | 75 | The code immediately gets paused in the debugger because... remember the `1000` iterations `for` loop with the debugger statements we ignored? Luckily, we can open up the console and skip all those iterations by directly modifying the loop variable and assigning `a = 999`. Stepping over a few times, we get the correct value of new `x` to be a bunch of characters with ascii values `130 30 10 154`. 76 | 77 |  78 | 79 | Now that we got the correct values, we can overwrite our `x` in the prettified code with these. 80 | 81 | ```js 82 | // x = h(str(x)); 83 | x = String.fromCharCode(130,30,10,154); 84 | ``` 85 | 86 | Next we have a regular expression being assigned to a variable `storage` and then since a regex is not a string, they wrote their own implementation of the `toString` function. 87 | 88 | In the `try` block, it is printing out the `source` onto the console and with `source` as the scope, executing `eval('eval(c(source,x))')`. 89 | 90 | Remember the weird `х` which was our input? I was kinda surprised the code never really used this input anywhere. Anyway, let's not jump to conclusions and see what the last line does. (Cuz this is where all the magic happens :wink:) 91 | 92 | Set a breakpoint at the `eval` statement and run the code with the input `CTF{qwerty}`. 93 | To my horror my browser just hanged. It didn't even hit the breakpoint we set. Weird. We know it executed successfully till the `source` variable was assigned. Maybe the `toString` is messing up? And we are right, it calls `c(source, x)` which performs a repeated xor on `source` with `x` (it's the 4 chars string) as the key. It is supposed to happen for `a.length` iterations but wait! Here, `a` is `source` which is a regex and a regular expression does not have a length property! So the loop just iterates infinitely and that explains why the browser *hanged*. 94 | 95 | Let's manually put a limit to it and see what the result looks like. 96 | Modify the `for` loop conditions to `(i = 0; i != 100; i++)` and run the code again. This time, we hit the breakpoint. Now set a new breakpoint in `c()` at the `return c` statement because we want to see the output of the first `eval`. After setting, continue and we'll hit the breakpoint at return. 97 | 98 |  99 | 100 | Interesting, it is returning the following expression 101 | 102 | ```js 103 | х==c('¢×&Ê´cʯ¬$¶³´}ÍÈ´T©Ð8ͳÍ|Ô÷aÈÐÝ&¨þJ',h(х))//᧢ï÷kï÷kï÷kï÷kï÷kï÷kï÷kï÷kï÷kï÷kï÷kï` 104 | ``` 105 | 106 | Out of curiosity I checked which `х` this is and sure enough, this is where our input was being used! 107 | Since this expression is being evaluated and returned to the `open_safe()` function, we need to somehow make it evaluate to `true`. Also our limit to the `for` loop is justified by the unnecessary comment in the end. 108 | 109 | So we need to find an input (instead of `qwerty`), that when hashed by `h()` and given as a key to `c()` along with the string containing the strange characters we got above, yields the input itself. One thing to note is that `c(a, b)` returns a string of length equal to that of `a`. So in this case, our input (the Cyrillic `х`) must be a string of length `39`. because the weird string '`¢×&Ê´cʯ¬$¶³´}ÍÈ´T©Ð8ͳÍ|Ô÷aÈÐÝ&¨þJ`' is of that length. 110 | 111 | Definitely not something we can brute force. At this point I couldn't think of anything else but try to understand how the function `h()` works. So here it is: 112 | 113 | ```js 114 | function h(s) { 115 | for (i = 0; i != s.length; i++) { 116 | a = ((typeof a == 'undefined' ? 1 : a) + ord(str(s[i]))) % 65521; 117 | b = ((typeof b == 'undefined' ? 0 : b) + a) % 65521; 118 | } 119 | return chr(b >> 8) + chr(b & 0xFF) + chr(a >> 8) + chr(a & 0xFF); 120 | } 121 | ``` 122 | 123 | The variable `a` seems to be simply the sum of the ascii values in the string mod `65521`. And the variable `b` seems to be similar but each value gets a weightage when you do the math. The initial values seem to be `1` and `0` respectively but that is only when `h()` is called the first time. Remember `x = h(str(x))`? That was the first time when it was called and towards the end of the loop, it had values `a = 2714` and `b = 33310` (run the original source again and step over till this statement). 124 | 125 |  126 | 127 | Finally, 4 characters are being concatenated and returned which are generated from these two numbers only. 128 | Then it struck me! We cannot possibly brute force the input but we can definitely brute force `a` and `b` which would yield all possible keys and we can pick the one which produces a valid english plaintext when passed through `c()`. Also, we need to make sure the resulting text produces the same values of `a` and `b` when passed through `h()`. 129 | I thought about the range of `a` and `b`, its `0 to 65521` and if my hunch is right, the `a` we are looking for must be in the range `2714 to 65521` and `b` in the range `33310 to 65521` *(because those were the initial values of `a` and `b` when `h()` was being called the second time)*. 130 | 131 | I quickly scripted this in python and wrote my own version of `c()` to know quickly if it generates a valid english alphabet result for a given `a` and `b`. Here's my script for that: [js_safe_.py](js_safe.py) 132 | ```py 133 | chars = string.ascii_uppercase + string.ascii_lowercase + string.digits + '_@!?-' 134 | 135 | def fast_c(a, b): 136 | for i in range(len(a)): 137 | xor = chr(ord(str(a[i])) ^ ord(str(b[i % len(b)]))) 138 | if not (xor in chars): 139 | return False 140 | return True 141 | ``` 142 | 143 | ```py 144 | weird = u'¢×&Ê´cʯ¬$¶³´}ÍÈ´T©Ð8ͳÍ|Ô÷aÈÐÝ&¨þJ' 145 | n = 65521 146 | 147 | for a in range(2714, n, 1): 148 | for b in range(33310, n, 1): 149 | sys.stdout.write('%d %d \r' % (a, b)) 150 | key = chr(b >> 8) + chr(b & 0xFF) + chr(a >> 8) + chr(a & 0xFF) 151 | if fast_c(weird, key): 152 | inp = c(weird, key) 153 | print('(%d, %d) => %s' % (a, b, inp)) 154 | ``` 155 | 156 | Run the script and after a while, we get the flag we are looking for! The first string doesn't make complete sense but the second one does! 157 | 158 |  159 | 160 | Just to be sure, I entered it as the password and voila! 161 | 162 |  163 | 164 | `CTF{_N3x7-v3R51ON-h45-AnTI-4NTi-ant1-D3bUg_}` 165 | 166 | At this point I was just admiring the beauty of this challenge. Create a hash of its own source code and use it as the key? Dynamically generate code during runtime and evaluate the input? Awesome! This was my first time debugging a JS web challenge and it was fun! 167 | 168 | Cheers to the CTF organizers at Google! :tada: 169 | -------------------------------------------------------------------------------- /Google CTF 2018/JS Safe 2.0/images/challenge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/JS Safe 2.0/images/challenge.png -------------------------------------------------------------------------------- /Google CTF 2018/JS Safe 2.0/images/img0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/JS Safe 2.0/images/img0.png -------------------------------------------------------------------------------- /Google CTF 2018/JS Safe 2.0/images/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/JS Safe 2.0/images/img1.png -------------------------------------------------------------------------------- /Google CTF 2018/JS Safe 2.0/images/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/JS Safe 2.0/images/img2.png -------------------------------------------------------------------------------- /Google CTF 2018/JS Safe 2.0/images/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/JS Safe 2.0/images/img3.png -------------------------------------------------------------------------------- /Google CTF 2018/JS Safe 2.0/images/img4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/JS Safe 2.0/images/img4.png -------------------------------------------------------------------------------- /Google CTF 2018/JS Safe 2.0/images/img5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/JS Safe 2.0/images/img5.png -------------------------------------------------------------------------------- /Google CTF 2018/JS Safe 2.0/images/img6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/JS Safe 2.0/images/img6.png -------------------------------------------------------------------------------- /Google CTF 2018/JS Safe 2.0/js_safe.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | import string 4 | import sys 5 | 6 | chars = string.ascii_uppercase + string.ascii_lowercase + string.digits + '_@!?-' 7 | weird = u'¢×&Ê´cʯ¬$¶³´}ÍÈ´T©Ð8ͳÍ|Ô÷aÈÐÝ&¨þJ' 8 | n = 65521 9 | 10 | # the pythonic version of c() from js_safe_2.html 11 | def c(a, b): 12 | c2 = '' 13 | for i in range(len(a)): 14 | c2 += chr(ord(str(a[i])) ^ ord(str(b[i % len(b)]))) 15 | return c2 16 | 17 | # the slightly faster version of c() which returns immediately if resulting char is not in the list of expected chars 18 | def fast_c(a, b): 19 | for i in range(len(a)): 20 | xor = chr(ord(str(a[i])) ^ ord(str(b[i % len(b)]))) 21 | if not (xor in chars): 22 | return False 23 | return True 24 | 25 | # started at a = 2714, b = 33310 26 | for a in range(5625, n, 1): 27 | for b in range(33310, n, 1): 28 | # a = 5625; b = 64921 29 | sys.stdout.write('%d %d \r' % (a, b)) 30 | key = chr(b >> 8) + chr(b & 0xFF) + chr(a >> 8) + chr(a & 0xFF) 31 | if fast_c(weird, key): 32 | inp = c(weird, key) 33 | print('(%d, %d) => %s' % (a, b, inp)) 34 | break # remove this 35 | 36 | # _N3x7-v3R51ON-h45-AnTI-4NTi-ant1-D3bUg_ 37 | -------------------------------------------------------------------------------- /Google CTF 2018/JS Safe 2.0/js_safe_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |JS safe v2.0 - the leading localStorage based safe solution with military grade JS anti-debug technology 6 | 14 | 98 | 101 | 116 | 117 | 118 |119 | 120 |121 |122 |131 |123 | 124 | 125 | 126 | 127 | 128 | 129 |130 |132 |133 |134 | 135 |136 | 137 | 138 | -------------------------------------------------------------------------------- /Google CTF 2018/Shall We Play a Game?/GameActivity.java: -------------------------------------------------------------------------------- 1 | package com.google.ctf.shallweplayagame; 2 | 3 | import android.animation.AnimatorSet; 4 | import android.os.Bundle; 5 | import android.support.v7.app.c; 6 | import android.view.View; 7 | import android.view.View.OnClickListener; 8 | import android.widget.LinearLayout; 9 | import android.widget.TextView; 10 | import java.lang.reflect.Array; 11 | import java.util.ArrayList; 12 | import java.util.LinkedList; 13 | import java.util.List; 14 | import java.util.Queue; 15 | import java.util.Random; 16 | 17 | public class GameActivity 18 | extends c 19 | implements View.OnClickListener 20 | { 21 | a[][] l = (a[][])Array.newInstance(a.class, new int[] { 3, 3 }); 22 | Queuem = new LinkedList(); 23 | Object n = N._(new Object[] { Integer.valueOf(3), N.h, Long.valueOf(1416127776L + 1869507705L + 544696686L + 1852403303L + 544042870L + 1696622963L + 544108404L + 544501536L + 1886151033L) }); 24 | int o; 25 | boolean p; 26 | byte[] q = new byte[32]; 27 | byte[] r = { -61, 15, 25, -115, -46, -11, 65, -3, 34, 93, -39, 98, 123, 17, 42, -121, 60, 40, -60, -112, 77, 111, 34, 14, -31, -4, -7, 66, 116, 108, 114, -122 }; 28 | 29 | public GameActivity() 30 | { 31 | N._(new Object[] { Integer.valueOf(3), N.i, n, q }); 32 | o = 0; 33 | p = false; 34 | } 35 | 36 | a a(List paramList) 37 | { 38 | return (a)paramList.get(((Random)n).nextInt(paramList.size())); 39 | } 40 | 41 | boolean a(a.a paramA) 42 | { 43 | int[] arrayOfInt1 = new int[3]; 44 | int[] tmp7_5 = arrayOfInt1; 45 | tmp7_5[0] = 0; 46 | int[] tmp11_7 = tmp7_5; 47 | tmp11_7[1] = 0; 48 | int[] tmp15_11 = tmp11_7; 49 | tmp15_11[2] = 0; 50 | tmp15_11; 51 | int[] arrayOfInt2 = new int[3]; 52 | int[] tmp27_25 = arrayOfInt2; 53 | tmp27_25[0] = 0; 54 | int[] tmp31_27 = tmp27_25; 55 | tmp31_27[1] = 0; 56 | int[] tmp35_31 = tmp31_27; 57 | tmp35_31[2] = 0; 58 | tmp35_31; 59 | int[] arrayOfInt3 = new int[2]; 60 | int[] tmp47_45 = arrayOfInt3; 61 | tmp47_45[0] = 0; 62 | int[] tmp51_47 = tmp47_45; 63 | tmp51_47[1] = 0; 64 | tmp51_47; 65 | int i = 0; 66 | while (i < 3) 67 | { 68 | j = 0; 69 | while (j < 3) 70 | { 71 | if (l[j][i].d == paramA) 72 | { 73 | arrayOfInt1[i] += 1; 74 | arrayOfInt2[j] += 1; 75 | if (i == j) { 76 | arrayOfInt3[0] += 1; 77 | } 78 | if (i + j == 2) { 79 | arrayOfInt3[1] += 1; 80 | } 81 | } 82 | j += 1; 83 | } 84 | i += 1; 85 | } 86 | int j = arrayOfInt1.length; 87 | i = 0; 88 | while (i < j) 89 | { 90 | if (arrayOfInt1[i] >= 3) { 91 | return true; 92 | } 93 | i += 1; 94 | } 95 | j = arrayOfInt2.length; 96 | i = 0; 97 | for (;;) 98 | { 99 | if (i >= j) { 100 | break label205; 101 | } 102 | if (arrayOfInt2[i] >= 3) { 103 | break; 104 | } 105 | i += 1; 106 | } 107 | label205: 108 | j = arrayOfInt3.length; 109 | i = 0; 110 | for (;;) 111 | { 112 | if (i >= j) { 113 | break label231; 114 | } 115 | if (arrayOfInt3[i] >= 3) { 116 | break; 117 | } 118 | i += 1; 119 | } 120 | label231: 121 | return false; 122 | } 123 | 124 | void k() 125 | { 126 | AnimatorSet localAnimatorSet = (AnimatorSet)m.poll(); 127 | if (localAnimatorSet != null) { 128 | localAnimatorSet.start(); 129 | } 130 | } 131 | 132 | List l() 133 | { 134 | ArrayList localArrayList = new ArrayList(); 135 | int i = 0; 136 | while (i < 3) 137 | { 138 | int j = 0; 139 | while (j < 3) 140 | { 141 | if (l[j][i].a()) { 142 | localArrayList.add(l[j][i]); 143 | } 144 | j += 1; 145 | } 146 | i += 1; 147 | } 148 | return localArrayList; 149 | } 150 | 151 | void m() 152 | { 153 | Object localObject1 = N._(new Object[] { Integer.valueOf(0), N.a, Integer.valueOf(0) }); 154 | Object localObject2 = N._(new Object[] { Integer.valueOf(1), N.b, q, Integer.valueOf(1) }); 155 | N._(new Object[] { Integer.valueOf(0), N.c, localObject1, Integer.valueOf(2), localObject2 }); 156 | localObject1 = (byte[])N._(new Object[] { Integer.valueOf(0), N.d, localObject1, r }); 157 | ((TextView)findViewById(2131165269)).setText(new String((byte[])localObject1)); 158 | o(); 159 | } 160 | 161 | void n() 162 | { 163 | int i = 0; 164 | while (i < 3) 165 | { 166 | int j = 0; 167 | while (j < 3) 168 | { 169 | l[j][i].a(a.a.a, 25); 170 | j += 1; 171 | } 172 | i += 1; 173 | } 174 | k(); 175 | o += 1; 176 | Object localObject = N._(new Object[] { Integer.valueOf(2), N.e, Integer.valueOf(2) }); 177 | N._(new Object[] { Integer.valueOf(2), N.f, localObject, q }); 178 | q = ((byte[])N._(new Object[] { Integer.valueOf(2), N.g, localObject })); 179 | if (o == 1000000) 180 | { 181 | m(); 182 | return; 183 | } 184 | ((TextView)findViewById(2131165269)).setText(String.format("%d / %d", new Object[] { Integer.valueOf(o), Integer.valueOf(1000000) })); 185 | } 186 | 187 | void o() 188 | { 189 | int i = 0; 190 | while (i < 3) 191 | { 192 | int j = 0; 193 | while (j < 3) 194 | { 195 | l[j][i].setValue(a.a.d); 196 | j += 1; 197 | } 198 | i += 1; 199 | } 200 | o = 0; 201 | p = true; 202 | k(); 203 | } 204 | 205 | public void onClick(View paramView) 206 | { 207 | if (p) {} 208 | while (!m.isEmpty()) { 209 | return; 210 | } 211 | paramView = (a)paramView; 212 | if (!paramView.a()) 213 | { 214 | b.b(); 215 | return; 216 | } 217 | b.a(); 218 | paramView.setValue(a.a.b); 219 | if (a(a.a.b)) 220 | { 221 | n(); 222 | return; 223 | } 224 | paramView = l(); 225 | if (paramView.isEmpty()) 226 | { 227 | n(); 228 | return; 229 | } 230 | a(paramView).setValue(a.a.c); 231 | if (a(a.a.c)) 232 | { 233 | o(); 234 | return; 235 | } 236 | k(); 237 | } 238 | 239 | protected void onCreate(Bundle paramBundle) 240 | { 241 | super.onCreate(paramBundle); 242 | setContentView(2131296283); 243 | paramBundle = (LinearLayout)findViewById(2131165268); 244 | int i = 0; 245 | while (i < 3) 246 | { 247 | LinearLayout localLinearLayout = new LinearLayout(getApplicationContext()); 248 | int j = 0; 249 | while (j < 3) 250 | { 251 | a localA = new a(getApplicationContext(), m); 252 | localLinearLayout.addView(localA); 253 | l[j][i] = localA; 254 | localA.setOnClickListener(this); 255 | j += 1; 256 | } 257 | paramBundle.addView(localLinearLayout); 258 | i += 1; 259 | } 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /Google CTF 2018/Shall We Play a Game?/Modded_GameActivity.smali: -------------------------------------------------------------------------------- 1 | .class public Lcom/google/ctf/shallweplayagame/GameActivity; 2 | .super Landroid/support/v7/app/c; 3 | 4 | # interfaces 5 | .implements Landroid/view/View$OnClickListener; 6 | 7 | 8 | # instance fields 9 | .field l:[[Lcom/google/ctf/shallweplayagame/a; 10 | 11 | .field m:Ljava/util/Queue; 12 | .annotation system Ldalvik/annotation/Signature; 13 | value = { 14 | "Ljava/util/Queue", 15 | "<", 16 | "Landroid/animation/AnimatorSet;", 17 | ">;" 18 | } 19 | .end annotation 20 | .end field 21 | 22 | .field n:Ljava/lang/Object; 23 | 24 | .field o:I 25 | 26 | .field p:Z 27 | 28 | .field q:[B 29 | 30 | .field r:[B 31 | 32 | 33 | # direct methods 34 | .method public constructor ()V 35 | .locals 23 36 | 37 | invoke-direct/range {p0 .. p0}, Landroid/support/v7/app/c;-> ()V 38 | 39 | const/16 v2, 0x20 40 | 41 | new-array v2, v2, [B 42 | 43 | fill-array-data v2, :array_0 44 | 45 | move-object/from16 v0, p0 46 | 47 | iput-object v2, v0, Lcom/google/ctf/shallweplayagame/GameActivity;->r:[B 48 | 49 | const/4 v2, 0x3 50 | 51 | const/4 v3, 0x3 52 | 53 | filled-new-array {v2, v3}, [I 54 | 55 | move-result-object v2 56 | 57 | const-class v3, Lcom/google/ctf/shallweplayagame/a; 58 | 59 | invoke-static {v3, v2}, Ljava/lang/reflect/Array;->newInstance(Ljava/lang/Class;[I)Ljava/lang/Object; 60 | 61 | move-result-object v2 62 | 63 | check-cast v2, [[Lcom/google/ctf/shallweplayagame/a; 64 | 65 | move-object/from16 v0, p0 66 | 67 | iput-object v2, v0, Lcom/google/ctf/shallweplayagame/GameActivity;->l:[[Lcom/google/ctf/shallweplayagame/a; 68 | 69 | new-instance v2, Ljava/util/LinkedList; 70 | 71 | invoke-direct {v2}, Ljava/util/LinkedList;-> ()V 72 | 73 | move-object/from16 v0, p0 74 | 75 | iput-object v2, v0, Lcom/google/ctf/shallweplayagame/GameActivity;->m:Ljava/util/Queue; 76 | 77 | const-wide/32 v2, 0x54686520 78 | 79 | const-wide/32 v4, 0x6f6e6c79 80 | 81 | const-wide/32 v6, 0x2077696e 82 | 83 | const-wide/32 v8, 0x6e696e67 84 | 85 | const-wide/32 v10, 0x206d6f76 86 | 87 | const-wide/32 v12, 0x65206973 88 | 89 | const-wide/32 v14, 0x206e6f74 90 | 91 | const-wide/32 v16, 0x20746f20 92 | 93 | const-wide/32 v18, 0x706c6179 94 | 95 | const/16 v20, 0x3 96 | 97 | move/from16 v0, v20 98 | 99 | new-array v0, v0, [Ljava/lang/Object; 100 | 101 | move-object/from16 v20, v0 102 | 103 | const/16 v21, 0x0 104 | 105 | const/16 v22, 0x3 106 | 107 | invoke-static/range {v22 .. v22}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 108 | 109 | move-result-object v22 110 | 111 | aput-object v22, v20, v21 112 | 113 | const/16 v21, 0x1 114 | 115 | sget-object v22, Lcom/google/ctf/shallweplayagame/N;->h:[I 116 | 117 | aput-object v22, v20, v21 118 | 119 | const/16 v21, 0x2 120 | 121 | add-long/2addr v2, v4 122 | 123 | add-long/2addr v2, v6 124 | 125 | add-long/2addr v2, v8 126 | 127 | add-long/2addr v2, v10 128 | 129 | add-long/2addr v2, v12 130 | 131 | add-long/2addr v2, v14 132 | 133 | add-long v2, v2, v16 134 | 135 | add-long v2, v2, v18 136 | 137 | invoke-static {v2, v3}, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long; 138 | 139 | move-result-object v2 140 | 141 | aput-object v2, v20, v21 142 | 143 | invoke-static/range {v20 .. v20}, Lcom/google/ctf/shallweplayagame/N;->_([Ljava/lang/Object;)Ljava/lang/Object; 144 | 145 | move-result-object v2 146 | 147 | move-object/from16 v0, p0 148 | 149 | iput-object v2, v0, Lcom/google/ctf/shallweplayagame/GameActivity;->n:Ljava/lang/Object; 150 | 151 | const/16 v2, 0x20 152 | 153 | new-array v2, v2, [B 154 | 155 | move-object/from16 v0, p0 156 | 157 | iput-object v2, v0, Lcom/google/ctf/shallweplayagame/GameActivity;->q:[B 158 | 159 | const/4 v2, 0x4 160 | 161 | new-array v2, v2, [Ljava/lang/Object; 162 | 163 | const/4 v3, 0x0 164 | 165 | const/4 v4, 0x3 166 | 167 | invoke-static {v4}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 168 | 169 | move-result-object v4 170 | 171 | aput-object v4, v2, v3 172 | 173 | const/4 v3, 0x1 174 | 175 | sget-object v4, Lcom/google/ctf/shallweplayagame/N;->i:[I 176 | 177 | aput-object v4, v2, v3 178 | 179 | const/4 v3, 0x2 180 | 181 | move-object/from16 v0, p0 182 | 183 | iget-object v4, v0, Lcom/google/ctf/shallweplayagame/GameActivity;->n:Ljava/lang/Object; 184 | 185 | aput-object v4, v2, v3 186 | 187 | const/4 v3, 0x3 188 | 189 | move-object/from16 v0, p0 190 | 191 | iget-object v4, v0, Lcom/google/ctf/shallweplayagame/GameActivity;->q:[B 192 | 193 | aput-object v4, v2, v3 194 | 195 | invoke-static {v2}, Lcom/google/ctf/shallweplayagame/N;->_([Ljava/lang/Object;)Ljava/lang/Object; 196 | 197 | const/4 v2, 0x0 198 | 199 | move-object/from16 v0, p0 200 | 201 | iput v2, v0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I 202 | 203 | const/4 v2, 0x0 204 | 205 | move-object/from16 v0, p0 206 | 207 | iput-boolean v2, v0, Lcom/google/ctf/shallweplayagame/GameActivity;->p:Z 208 | 209 | return-void 210 | 211 | :array_0 212 | .array-data 1 213 | -0x3dt 214 | 0xft 215 | 0x19t 216 | -0x73t 217 | -0x2et 218 | -0xbt 219 | 0x41t 220 | -0x3t 221 | 0x22t 222 | 0x5dt 223 | -0x27t 224 | 0x62t 225 | 0x7bt 226 | 0x11t 227 | 0x2at 228 | -0x79t 229 | 0x3ct 230 | 0x28t 231 | -0x3ct 232 | -0x70t 233 | 0x4dt 234 | 0x6ft 235 | 0x22t 236 | 0xet 237 | -0x1ft 238 | -0x4t 239 | -0x7t 240 | 0x42t 241 | 0x74t 242 | 0x6ct 243 | 0x72t 244 | -0x7at 245 | .end array-data 246 | .end method 247 | 248 | 249 | # virtual methods 250 | .method a(Ljava/util/List;)Lcom/google/ctf/shallweplayagame/a; 251 | .locals 2 252 | .annotation system Ldalvik/annotation/Signature; 253 | value = { 254 | "(", 255 | "Ljava/util/List", 256 | "<", 257 | "Lcom/google/ctf/shallweplayagame/a;", 258 | ">;)", 259 | "Lcom/google/ctf/shallweplayagame/a;" 260 | } 261 | .end annotation 262 | 263 | iget-object v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->n:Ljava/lang/Object; 264 | 265 | check-cast v0, Ljava/util/Random; 266 | 267 | invoke-interface {p1}, Ljava/util/List;->size()I 268 | 269 | move-result v1 270 | 271 | invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I 272 | 273 | move-result v0 274 | 275 | invoke-interface {p1, v0}, Ljava/util/List;->get(I)Ljava/lang/Object; 276 | 277 | move-result-object v0 278 | 279 | check-cast v0, Lcom/google/ctf/shallweplayagame/a; 280 | 281 | return-object v0 282 | .end method 283 | 284 | .method a(Lcom/google/ctf/shallweplayagame/a$a;)Z 285 | .locals 10 286 | 287 | const/4 v9, 0x2 288 | 289 | const/4 v0, 0x1 290 | 291 | const/4 v8, 0x3 292 | 293 | const/4 v1, 0x0 294 | 295 | new-array v4, v8, [I 296 | 297 | fill-array-data v4, :array_0 298 | 299 | new-array v5, v8, [I 300 | 301 | fill-array-data v5, :array_1 302 | 303 | new-array v6, v9, [I 304 | 305 | fill-array-data v6, :array_2 306 | 307 | move v3, v1 308 | 309 | :goto_0 310 | if-ge v3, v8, :cond_3 311 | 312 | move v2, v1 313 | 314 | :goto_1 315 | if-ge v2, v8, :cond_2 316 | 317 | iget-object v7, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->l:[[Lcom/google/ctf/shallweplayagame/a; 318 | 319 | aget-object v7, v7, v2 320 | 321 | aget-object v7, v7, v3 322 | 323 | iget-object v7, v7, Lcom/google/ctf/shallweplayagame/a;->d:Lcom/google/ctf/shallweplayagame/a$a; 324 | 325 | if-ne v7, p1, :cond_1 326 | 327 | aget v7, v4, v3 328 | 329 | add-int/lit8 v7, v7, 0x1 330 | 331 | aput v7, v4, v3 332 | 333 | aget v7, v5, v2 334 | 335 | add-int/lit8 v7, v7, 0x1 336 | 337 | aput v7, v5, v2 338 | 339 | if-ne v3, v2, :cond_0 340 | 341 | aget v7, v6, v1 342 | 343 | add-int/lit8 v7, v7, 0x1 344 | 345 | aput v7, v6, v1 346 | 347 | :cond_0 348 | add-int v7, v3, v2 349 | 350 | if-ne v7, v9, :cond_1 351 | 352 | aget v7, v6, v0 353 | 354 | add-int/lit8 v7, v7, 0x1 355 | 356 | aput v7, v6, v0 357 | 358 | :cond_1 359 | add-int/lit8 v2, v2, 0x1 360 | 361 | goto :goto_1 362 | 363 | :cond_2 364 | add-int/lit8 v2, v3, 0x1 365 | 366 | move v3, v2 367 | 368 | goto :goto_0 369 | 370 | :cond_3 371 | array-length v3, v4 372 | 373 | move v2, v1 374 | 375 | :goto_2 376 | if-ge v2, v3, :cond_6 377 | 378 | aget v7, v4, v2 379 | 380 | if-lt v7, v8, :cond_5 381 | 382 | :cond_4 383 | :goto_3 384 | return v0 385 | 386 | :cond_5 387 | add-int/lit8 v2, v2, 0x1 388 | 389 | goto :goto_2 390 | 391 | :cond_6 392 | array-length v3, v5 393 | 394 | move v2, v1 395 | 396 | :goto_4 397 | if-ge v2, v3, :cond_7 398 | 399 | aget v4, v5, v2 400 | 401 | if-ge v4, v8, :cond_4 402 | 403 | add-int/lit8 v2, v2, 0x1 404 | 405 | goto :goto_4 406 | 407 | :cond_7 408 | array-length v3, v6 409 | 410 | move v2, v1 411 | 412 | :goto_5 413 | if-ge v2, v3, :cond_8 414 | 415 | aget v4, v6, v2 416 | 417 | if-ge v4, v8, :cond_4 418 | 419 | add-int/lit8 v2, v2, 0x1 420 | 421 | goto :goto_5 422 | 423 | :cond_8 424 | move v0, v1 425 | 426 | goto :goto_3 427 | 428 | :array_0 429 | .array-data 4 430 | 0x0 431 | 0x0 432 | 0x0 433 | .end array-data 434 | 435 | :array_1 436 | .array-data 4 437 | 0x0 438 | 0x0 439 | 0x0 440 | .end array-data 441 | 442 | :array_2 443 | .array-data 4 444 | 0x0 445 | 0x0 446 | .end array-data 447 | .end method 448 | 449 | .method k()V 450 | .locals 1 451 | 452 | iget-object v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->m:Ljava/util/Queue; 453 | 454 | invoke-interface {v0}, Ljava/util/Queue;->poll()Ljava/lang/Object; 455 | 456 | move-result-object v0 457 | 458 | check-cast v0, Landroid/animation/AnimatorSet; 459 | 460 | if-eqz v0, :cond_0 461 | 462 | invoke-virtual {v0}, Landroid/animation/AnimatorSet;->start()V 463 | 464 | :cond_0 465 | return-void 466 | .end method 467 | 468 | .method l()Ljava/util/List; 469 | .locals 6 470 | .annotation system Ldalvik/annotation/Signature; 471 | value = { 472 | "()", 473 | "Ljava/util/List", 474 | "<", 475 | "Lcom/google/ctf/shallweplayagame/a;", 476 | ">;" 477 | } 478 | .end annotation 479 | 480 | const/4 v5, 0x3 481 | 482 | const/4 v1, 0x0 483 | 484 | new-instance v3, Ljava/util/ArrayList; 485 | 486 | invoke-direct {v3}, Ljava/util/ArrayList;-> ()V 487 | 488 | move v2, v1 489 | 490 | :goto_0 491 | if-ge v2, v5, :cond_2 492 | 493 | move v0, v1 494 | 495 | :goto_1 496 | if-ge v0, v5, :cond_1 497 | 498 | iget-object v4, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->l:[[Lcom/google/ctf/shallweplayagame/a; 499 | 500 | aget-object v4, v4, v0 501 | 502 | aget-object v4, v4, v2 503 | 504 | invoke-virtual {v4}, Lcom/google/ctf/shallweplayagame/a;->a()Z 505 | 506 | move-result v4 507 | 508 | if-eqz v4, :cond_0 509 | 510 | iget-object v4, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->l:[[Lcom/google/ctf/shallweplayagame/a; 511 | 512 | aget-object v4, v4, v0 513 | 514 | aget-object v4, v4, v2 515 | 516 | invoke-interface {v3, v4}, Ljava/util/List;->add(Ljava/lang/Object;)Z 517 | 518 | :cond_0 519 | add-int/lit8 v0, v0, 0x1 520 | 521 | goto :goto_1 522 | 523 | :cond_1 524 | add-int/lit8 v0, v2, 0x1 525 | 526 | move v2, v0 527 | 528 | goto :goto_0 529 | 530 | :cond_2 531 | return-object v3 532 | .end method 533 | 534 | .method m()V 535 | .locals 9 536 | 537 | const/4 v8, 0x4 538 | 539 | const/4 v7, 0x3 540 | 541 | const/4 v6, 0x2 542 | 543 | const/4 v5, 0x1 544 | 545 | const/4 v4, 0x0 546 | 547 | new-array v0, v7, [Ljava/lang/Object; 548 | 549 | invoke-static {v4}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 550 | 551 | move-result-object v1 552 | 553 | aput-object v1, v0, v4 554 | 555 | sget-object v1, Lcom/google/ctf/shallweplayagame/N;->a:[I 556 | 557 | aput-object v1, v0, v5 558 | 559 | invoke-static {v4}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 560 | 561 | move-result-object v1 562 | 563 | aput-object v1, v0, v6 564 | 565 | invoke-static {v0}, Lcom/google/ctf/shallweplayagame/N;->_([Ljava/lang/Object;)Ljava/lang/Object; 566 | 567 | move-result-object v0 568 | 569 | new-array v1, v8, [Ljava/lang/Object; 570 | 571 | invoke-static {v5}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 572 | 573 | move-result-object v2 574 | 575 | aput-object v2, v1, v4 576 | 577 | sget-object v2, Lcom/google/ctf/shallweplayagame/N;->b:[I 578 | 579 | aput-object v2, v1, v5 580 | 581 | iget-object v2, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->q:[B 582 | 583 | aput-object v2, v1, v6 584 | 585 | invoke-static {v5}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 586 | 587 | move-result-object v2 588 | 589 | aput-object v2, v1, v7 590 | 591 | invoke-static {v1}, Lcom/google/ctf/shallweplayagame/N;->_([Ljava/lang/Object;)Ljava/lang/Object; 592 | 593 | move-result-object v1 594 | 595 | const/4 v2, 0x5 596 | 597 | new-array v2, v2, [Ljava/lang/Object; 598 | 599 | invoke-static {v4}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 600 | 601 | move-result-object v3 602 | 603 | aput-object v3, v2, v4 604 | 605 | sget-object v3, Lcom/google/ctf/shallweplayagame/N;->c:[I 606 | 607 | aput-object v3, v2, v5 608 | 609 | aput-object v0, v2, v6 610 | 611 | invoke-static {v6}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 612 | 613 | move-result-object v3 614 | 615 | aput-object v3, v2, v7 616 | 617 | aput-object v1, v2, v8 618 | 619 | invoke-static {v2}, Lcom/google/ctf/shallweplayagame/N;->_([Ljava/lang/Object;)Ljava/lang/Object; 620 | 621 | new-array v1, v8, [Ljava/lang/Object; 622 | 623 | invoke-static {v4}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 624 | 625 | move-result-object v2 626 | 627 | aput-object v2, v1, v4 628 | 629 | sget-object v2, Lcom/google/ctf/shallweplayagame/N;->d:[I 630 | 631 | aput-object v2, v1, v5 632 | 633 | aput-object v0, v1, v6 634 | 635 | iget-object v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->r:[B 636 | 637 | aput-object v0, v1, v7 638 | 639 | invoke-static {v1}, Lcom/google/ctf/shallweplayagame/N;->_([Ljava/lang/Object;)Ljava/lang/Object; 640 | 641 | move-result-object v0 642 | 643 | check-cast v0, [B 644 | 645 | check-cast v0, [B 646 | 647 | const v1, 0x7f070055 648 | 649 | invoke-virtual {p0, v1}, Lcom/google/ctf/shallweplayagame/GameActivity;->findViewById(I)Landroid/view/View; 650 | 651 | move-result-object v1 652 | 653 | check-cast v1, Landroid/widget/TextView; 654 | 655 | new-instance v2, Ljava/lang/String; 656 | 657 | invoke-direct {v2, v0}, Ljava/lang/String;-> ([B)V 658 | 659 | invoke-virtual {v1, v2}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V 660 | 661 | invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->o()V 662 | 663 | return-void 664 | .end method 665 | 666 | .method n()V 667 | .locals 10 668 | 669 | const v9, 0xf4240 670 | 671 | const/4 v8, 0x1 672 | 673 | const/4 v7, 0x3 674 | 675 | const/4 v1, 0x0 676 | 677 | const/4 v6, 0x2 678 | 679 | move v2, v1 680 | 681 | :goto_0 682 | if-ge v2, v7, :cond_1 683 | 684 | move v0, v1 685 | 686 | :goto_1 687 | if-ge v0, v7, :cond_0 688 | 689 | iget-object v3, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->l:[[Lcom/google/ctf/shallweplayagame/a; 690 | 691 | aget-object v3, v3, v0 692 | 693 | aget-object v3, v3, v2 694 | 695 | sget-object v4, Lcom/google/ctf/shallweplayagame/a$a;->a:Lcom/google/ctf/shallweplayagame/a$a; 696 | 697 | const/16 v5, 0x0 698 | 699 | invoke-virtual {v3, v4, v5}, Lcom/google/ctf/shallweplayagame/a;->a(Lcom/google/ctf/shallweplayagame/a$a;I)V 700 | 701 | add-int/lit8 v0, v0, 0x1 702 | 703 | goto :goto_1 704 | 705 | :cond_0 706 | add-int/lit8 v0, v2, 0x1 707 | 708 | move v2, v0 709 | 710 | goto :goto_0 711 | 712 | :cond_1 713 | invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->k()V 714 | 715 | const v5, 0xf423f # Max iterations 716 | const v4, 0x0 # Loop counter 717 | :goto_6 718 | if-ge v4, v5, :cond_2 719 | # -------------------------------------------START LOOP--------------------------------------------------- 720 | # Increment o 721 | iget v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I 722 | add-int/lit8 v0, v0, 0x1 723 | iput v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I 724 | 725 | # Flag decryption start 726 | new-array v0, v7, [Ljava/lang/Object; 727 | invoke-static {v6}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 728 | move-result-object v2 729 | aput-object v2, v0, v1 730 | sget-object v2, Lcom/google/ctf/shallweplayagame/N;->e:[I 731 | aput-object v2, v0, v8 732 | invoke-static {v6}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 733 | move-result-object v2 734 | aput-object v2, v0, v6 735 | invoke-static {v0}, Lcom/google/ctf/shallweplayagame/N;->_([Ljava/lang/Object;)Ljava/lang/Object; 736 | move-result-object v0 737 | const/4 v2, 0x4 738 | new-array v2, v2, [Ljava/lang/Object; 739 | invoke-static {v6}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 740 | move-result-object v3 741 | aput-object v3, v2, v1 742 | sget-object v3, Lcom/google/ctf/shallweplayagame/N;->f:[I 743 | aput-object v3, v2, v8 744 | aput-object v0, v2, v6 745 | iget-object v3, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->q:[B 746 | aput-object v3, v2, v7 747 | invoke-static {v2}, Lcom/google/ctf/shallweplayagame/N;->_([Ljava/lang/Object;)Ljava/lang/Object; 748 | new-array v2, v7, [Ljava/lang/Object; 749 | invoke-static {v6}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 750 | move-result-object v3 751 | aput-object v3, v2, v1 752 | sget-object v3, Lcom/google/ctf/shallweplayagame/N;->g:[I 753 | aput-object v3, v2, v8 754 | aput-object v0, v2, v6 755 | invoke-static {v2}, Lcom/google/ctf/shallweplayagame/N;->_([Ljava/lang/Object;)Ljava/lang/Object; 756 | move-result-object v0 757 | check-cast v0, [B 758 | check-cast v0, [B 759 | iput-object v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->q:[B 760 | # Flag decryption end 761 | 762 | # If condition 763 | iget v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I 764 | if-ne v0, v9, :cond_3 765 | # If block 766 | invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->m()V 767 | :goto_2 768 | return-void 769 | # Else block 770 | :cond_3 771 | add-int/lit8 v4, v4, 0x1 # Increment counter 772 | goto :goto_6 773 | # --------------------------------------------END LOOP-------------------------------------------------- 774 | :cond_2 775 | const v0, 0x7f070055 776 | 777 | invoke-virtual {p0, v0}, Lcom/google/ctf/shallweplayagame/GameActivity;->findViewById(I)Landroid/view/View; 778 | 779 | move-result-object v0 780 | 781 | check-cast v0, Landroid/widget/TextView; 782 | 783 | const-string v2, "%d / %d" 784 | 785 | new-array v3, v6, [Ljava/lang/Object; 786 | 787 | iget v4, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I 788 | 789 | invoke-static {v4}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 790 | 791 | move-result-object v4 792 | 793 | aput-object v4, v3, v1 794 | 795 | invoke-static {v9}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer; 796 | 797 | move-result-object v1 798 | 799 | aput-object v1, v3, v8 800 | 801 | invoke-static {v2, v3}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String; 802 | 803 | move-result-object v1 804 | 805 | invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V 806 | 807 | goto :goto_2 808 | .end method 809 | 810 | .method o()V 811 | .locals 6 812 | 813 | const/4 v5, 0x3 814 | 815 | const/4 v1, 0x0 816 | 817 | move v2, v1 818 | 819 | :goto_0 820 | if-ge v2, v5, :cond_1 821 | 822 | move v0, v1 823 | 824 | :goto_1 825 | if-ge v0, v5, :cond_0 826 | 827 | iget-object v3, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->l:[[Lcom/google/ctf/shallweplayagame/a; 828 | 829 | aget-object v3, v3, v0 830 | 831 | aget-object v3, v3, v2 832 | 833 | sget-object v4, Lcom/google/ctf/shallweplayagame/a$a;->d:Lcom/google/ctf/shallweplayagame/a$a; 834 | 835 | invoke-virtual {v3, v4}, Lcom/google/ctf/shallweplayagame/a;->setValue(Lcom/google/ctf/shallweplayagame/a$a;)V 836 | 837 | add-int/lit8 v0, v0, 0x1 838 | 839 | goto :goto_1 840 | 841 | :cond_0 842 | add-int/lit8 v0, v2, 0x1 843 | 844 | move v2, v0 845 | 846 | goto :goto_0 847 | 848 | :cond_1 849 | iput v1, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I 850 | 851 | const/4 v0, 0x1 852 | 853 | iput-boolean v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->p:Z 854 | 855 | invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->k()V 856 | 857 | return-void 858 | .end method 859 | 860 | .method public onClick(Landroid/view/View;)V 861 | .locals 2 862 | 863 | iget-boolean v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->p:Z 864 | 865 | if-eqz v0, :cond_1 866 | 867 | :cond_0 868 | :goto_0 869 | return-void 870 | 871 | :cond_1 872 | iget-object v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->m:Ljava/util/Queue; 873 | 874 | invoke-interface {v0}, Ljava/util/Queue;->isEmpty()Z 875 | 876 | move-result v0 877 | 878 | if-eqz v0, :cond_0 879 | 880 | check-cast p1, Lcom/google/ctf/shallweplayagame/a; 881 | 882 | invoke-virtual {p1}, Lcom/google/ctf/shallweplayagame/a;->a()Z 883 | 884 | move-result v0 885 | 886 | if-nez v0, :cond_2 887 | 888 | invoke-static {}, Lcom/google/ctf/shallweplayagame/b;->b()V 889 | 890 | goto :goto_0 891 | 892 | :cond_2 893 | invoke-static {}, Lcom/google/ctf/shallweplayagame/b;->a()V 894 | 895 | sget-object v0, Lcom/google/ctf/shallweplayagame/a$a;->b:Lcom/google/ctf/shallweplayagame/a$a; 896 | 897 | invoke-virtual {p1, v0}, Lcom/google/ctf/shallweplayagame/a;->setValue(Lcom/google/ctf/shallweplayagame/a$a;)V 898 | 899 | # sget-object v0, Lcom/google/ctf/shallweplayagame/a$a;->b:Lcom/google/ctf/shallweplayagame/a$a; 900 | 901 | # invoke-virtual {p0, v0}, Lcom/google/ctf/shallweplayagame/GameActivity;->a(Lcom/google/ctf/shallweplayagame/a$a;)Z 902 | 903 | # move-result v0 904 | 905 | # if-eqz v0, :cond_3 906 | 907 | # invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->n()V 908 | 909 | # goto :goto_0 910 | 911 | :cond_3 912 | invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->l()Ljava/util/List; 913 | 914 | move-result-object v0 915 | 916 | invoke-interface {v0}, Ljava/util/List;->isEmpty()Z 917 | 918 | move-result v1 919 | 920 | if-eqz v1, :cond_4 921 | 922 | invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->n()V 923 | 924 | goto :goto_0 925 | 926 | :cond_4 927 | invoke-virtual {p0, v0}, Lcom/google/ctf/shallweplayagame/GameActivity;->a(Ljava/util/List;)Lcom/google/ctf/shallweplayagame/a; 928 | 929 | move-result-object v0 930 | 931 | sget-object v1, Lcom/google/ctf/shallweplayagame/a$a;->b:Lcom/google/ctf/shallweplayagame/a$a; 932 | 933 | invoke-virtual {v0, v1}, Lcom/google/ctf/shallweplayagame/a;->setValue(Lcom/google/ctf/shallweplayagame/a$a;)V 934 | 935 | # sget-object v0, Lcom/google/ctf/shallweplayagame/a$a;->b:Lcom/google/ctf/shallweplayagame/a$a; 936 | 937 | # invoke-virtual {p0, v0}, Lcom/google/ctf/shallweplayagame/GameActivity;->a(Lcom/google/ctf/shallweplayagame/a$a;)Z 938 | 939 | # move-result v0 940 | 941 | # if-eqz v0, :cond_5 942 | 943 | # invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->n()V 944 | 945 | # goto :goto_0 946 | 947 | :cond_5 948 | invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->k()V 949 | 950 | goto :goto_0 951 | .end method 952 | 953 | .method protected onCreate(Landroid/os/Bundle;)V 954 | .locals 9 955 | 956 | const/4 v8, 0x3 957 | 958 | const/4 v2, 0x0 959 | 960 | invoke-super {p0, p1}, Landroid/support/v7/app/c;->onCreate(Landroid/os/Bundle;)V 961 | 962 | const v0, 0x7f09001b 963 | 964 | invoke-virtual {p0, v0}, Lcom/google/ctf/shallweplayagame/GameActivity;->setContentView(I)V 965 | 966 | const v0, 0x7f070054 967 | 968 | invoke-virtual {p0, v0}, Lcom/google/ctf/shallweplayagame/GameActivity;->findViewById(I)Landroid/view/View; 969 | 970 | move-result-object v0 971 | 972 | check-cast v0, Landroid/widget/LinearLayout; 973 | 974 | move v3, v2 975 | 976 | :goto_0 977 | if-ge v3, v8, :cond_1 978 | 979 | new-instance v4, Landroid/widget/LinearLayout; 980 | 981 | invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->getApplicationContext()Landroid/content/Context; 982 | 983 | move-result-object v1 984 | 985 | invoke-direct {v4, v1}, Landroid/widget/LinearLayout;-> (Landroid/content/Context;)V 986 | 987 | move v1, v2 988 | 989 | :goto_1 990 | if-ge v1, v8, :cond_0 991 | 992 | new-instance v5, Lcom/google/ctf/shallweplayagame/a; 993 | 994 | invoke-virtual {p0}, Lcom/google/ctf/shallweplayagame/GameActivity;->getApplicationContext()Landroid/content/Context; 995 | 996 | move-result-object v6 997 | 998 | iget-object v7, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->m:Ljava/util/Queue; 999 | 1000 | invoke-direct {v5, v6, v7}, Lcom/google/ctf/shallweplayagame/a;-> (Landroid/content/Context;Ljava/util/Queue;)V 1001 | 1002 | invoke-virtual {v4, v5}, Landroid/widget/LinearLayout;->addView(Landroid/view/View;)V 1003 | 1004 | iget-object v6, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->l:[[Lcom/google/ctf/shallweplayagame/a; 1005 | 1006 | aget-object v6, v6, v1 1007 | 1008 | aput-object v5, v6, v3 1009 | 1010 | invoke-virtual {v5, p0}, Lcom/google/ctf/shallweplayagame/a;->setOnClickListener(Landroid/view/View$OnClickListener;)V 1011 | 1012 | add-int/lit8 v1, v1, 0x1 1013 | 1014 | goto :goto_1 1015 | 1016 | :cond_0 1017 | invoke-virtual {v0, v4}, Landroid/widget/LinearLayout;->addView(Landroid/view/View;)V 1018 | 1019 | add-int/lit8 v1, v3, 0x1 1020 | 1021 | move v3, v1 1022 | 1023 | goto :goto_0 1024 | 1025 | :cond_1 1026 | return-void 1027 | .end method 1028 | -------------------------------------------------------------------------------- /Google CTF 2018/Shall We Play a Game?/README.md: -------------------------------------------------------------------------------- 1 | # Shall We Play a Game? - 113 2 | 3 | ## The Challenge 4 | Here's the challenge task from the reversing category: 5 | 6 |  7 | 8 | 9 | 10 | [app.apk](./app.apk) 11 | 12 | 13 | 14 | ## Introduction 15 | 16 | Just a heads up, I am completely new to reversing and especially apk rev. So I initially avoided this challenge but later decided to hit it as it seemed to be the easiest challenge the CTF. 17 | 18 | As I said, this was my first apk rev challenge. The apk won't even run on my phone so I had to try on other phones to see what it is about. Now based on what I found, the basic approach to reversing an apk goes like this: 19 | 20 | * To understand the apk 21 | 1. Convert the apk to a jar file 22 | 2. Decompile the jar to extract the java class files 23 | 3. Get a rough idea of the working of the apk from these source files 24 | * To modify the apk 25 | 1. Decompile the apk again but this time to extract .smali files 26 | 2. Modify the .smali files 27 | 3. Build the apk 28 | 4. Sign the apk and deploy it 29 | 30 | I converted the apk to a jar using `dex2jar` and then decompiled the jar using online decompilers. 31 | The modification of the apk can be done using `apktool` 32 | and you can sign it using `jarsigner`. 33 | 34 | ## The Overview 35 | 36 | On installing and running the app, we get the famous tic-tac-toe game (also called Noughts and Crosses). When you win/tie against the CPU, it resets the board and displays `1/1000000`. Meaning we have to play a million games to get the flag just like the challenge said. 37 | 38 |  39 | 40 | Playing the game a million times is clearly absurd and we have to trick the app into thinking we played those many times. 41 | 42 | I jumped into the source code of the app given in `GameActivity.java` that we get after decompiling the jar. I search for `1000000` and find it in this function called `n()`. 43 | 44 | ```java 45 | void n() 46 | { 47 | int i = 0; 48 | while (i < 3) 49 | { 50 | int j = 0; 51 | while (j < 3) 52 | { 53 | l[j][i].a(a.a.a, 25); 54 | j += 1; 55 | } 56 | i += 1; 57 | } 58 | k(); 59 | o += 1; 60 | Object localObject = N._(new Object[] { Integer.valueOf(2), N.e, Integer.valueOf(2) }); 61 | N._(new Object[] { Integer.valueOf(2), N.f, localObject, q }); 62 | q = ((byte[])N._(new Object[] { Integer.valueOf(2), N.g, localObject })); 63 | if (o == 1000000) 64 | { 65 | m(); 66 | return; 67 | } 68 | ((TextView)findViewById(2131165269)).setText(String.format("%d / %d", new Object[] { Integer.valueOf(o), Integer.valueOf(1000000) })); 69 | } 70 | ``` 71 | 72 | There are 2 occurrences of the number. The first, in an `if` which evaluates to `true` only if `o` equals `1000000` meaning the `o` must be the counter as to how many games we won. And the second occurrence is in the text that displays our progress when we win a game. 73 | 74 | This means that the method `n()` is being called every time we win a game. Since it was an 'easy' challenge I assumed maybe changing the one million to just ten or one would give us our flag; or maybe find where the variable `o` is being initialized and give it a an initial value of `999999` so we can directly get to `1000000`. Or better yet, directly call the flag printing method `m()`. 75 | 76 | Unfortunately it's not *that* easy. It printed gibberish. 77 | 78 |  79 | 80 | We can conclude one thing from this. The flag seems to be decrypted bit by bit after every winning game and we can't skip any games. We have to *'play'* **exactly** a million times to get the flag. Let's go ahead and change the definition of *'play'* then :wink:. 81 | 82 | ## The Solution 83 | 84 | We know that `n()` is being called every time we win so the decryption part must be here. Neither the two `while` loops, nor the call to `k()` (which I think is for the animation at the end) look interesting but the following lines definitely look suspicious. 85 | 86 | ```java 87 | Object localObject = N._(new Object[] { Integer.valueOf(2), N.e, Integer.valueOf(2) }); 88 | N._(new Object[] { Integer.valueOf(2), N.f, localObject, q }); 89 | q = ((byte[])N._(new Object[] { Integer.valueOf(2), N.g, localObject })); 90 | ``` 91 | 92 | These lines must be decrypting the flag after every game. Only if we can find a way to put it in a loop. That way we only need to win a few times to get the flag. 93 | 94 | Basically, my idea was to modify the code to something like this: 95 | 96 | ```java 97 | void n() { 98 | ... // The animations code 99 | 100 | while (true) { 101 | o += 1; 102 | Object localObject = N._(new Object[] { Integer.valueOf(2), N.e, Integer.valueOf(2) }); 103 | N._(new Object[] { Integer.valueOf(2), N.f, localObject, q }); 104 | q = ((byte[])N._(new Object[] { Integer.valueOf(2), N.g, localObject })); 105 | if (o == 1000000) 106 | { 107 | m(); 108 | return; 109 | } 110 | } 111 | 112 | ... // The progress display code 113 | } 114 | ``` 115 | 116 | I looked at its corresponding smali code in the `GameActivity.smali` file. It was kinda scary but eventually found my way to the method `n()` and the code we need to put in the loop. 117 | 118 |  119 | 120 | 121 | I realized loops are implemented with simple `goto` statements like in assembly. So I inserted a `:goto_6` in the beginning and modified `if-ne v0, v9, :cond_2` to `if-ne v0, v9, :goto_6` so that the loop continues if `o` is not equal to a `million`. 122 | 123 | I rebuilt the apk, signed it and installed it on my phone. As soon as I played the winning move of the first game, the game seemed to have hanged but after waiting patiently for a minute or two, we get the flag! 124 | 125 |  126 | 127 | ### **`CTF{ThLssOfInncncIsThPrcOfAppls}`** 128 | 129 | --- 130 | 131 | ## Extras 132 | 133 | It was a fun challenge for me and here are some minor tweaks I came across while solving the challenge: 134 | 135 | * ### Fixing the hang 136 | We can remove the *hang* by replacing the `while` loop with a `for` and constraining it to a limited number of iterations after every winning game. This can be done with the following smali code: 137 | 138 | ```python 139 | 140 | const v5, 0xffff # Max iterations 141 | const v4, 0x0 # Loop counter 142 | :goto_6 143 | if-ge v4, v5, :cond_2 # Break out of the loop 144 | ... 145 | ... 146 | add-int/lit8 v4, v4, 0x1 # Increment counter 147 | goto :goto_6 148 | ``` 149 | 150 | * ### CPU plays X too 151 | While solving the challenge initially, I wanted to speed up the gameplay. So I thought of changing the CPU's choice to `X` too so that we can never lose. This logic was implemented in the `onClick()` method. 152 | ```java 153 | public void onClick(View paramView) { 154 | if (p) {} 155 | while (!m.isEmpty()) { 156 | return; 157 | } 158 | paramView = (a)paramView; 159 | if (!paramView.a()) { 160 | b.b(); 161 | return; 162 | } 163 | b.a(); 164 | paramView.setValue(a.a.b); 165 | if (a(a.a.b)) { 166 | n(); 167 | return; 168 | } 169 | paramView = l(); 170 | if (paramView.isEmpty()) { 171 | n(); 172 | return; 173 | } 174 | a(paramView).setValue(a.a.c); 175 | if (a(a.a.c)) { 176 | o(); 177 | return; 178 | } 179 | k(); 180 | } 181 | ``` 182 | 183 | When I changed all occurrences of `(a.a.c)` to `(a.a.b)` like in the `a(paramView).setValue()` line, the CPU started playing `X` too! (Don't forget to replace the call to `o()` with `n()` cuz you wanna win when the CPU wins too!) 184 | 185 | Here's a screenshot of the modded apk where CPU plays X and you have to fill the board to win! 186 | 187 |  188 | 189 | * ### One move to win 190 | Wouldn't it be a lot better if you only need to tap once to win the game instead of getting 3 continuously? That's what I thought and this is simple to do. Simply remove the `if(a(a.a.b))` condition and just call `n()` in the `onClick()` method. 191 | 192 | This definitely speeds up the game but the annoying animations were taking so long. So I sped up those too by navigating to the `n()` method in the smali file and changing the line inside the two `while` loops from `const/16 v5, 0x19` to `const/16 v5, 0x0`. 193 | 194 | The modded apk can be found [here](modded.apk) in which the CPU plays X and you have to wait a few minutes for the flag to decrypt. Its corresponding smali can be found [here](Modded_GameActivity.smali). 195 | 196 | -------------------------------------------------------------------------------- /Google CTF 2018/Shall We Play a Game?/app.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/Shall We Play a Game?/app.apk -------------------------------------------------------------------------------- /Google CTF 2018/Shall We Play a Game?/images/challenge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/Shall We Play a Game?/images/challenge.png -------------------------------------------------------------------------------- /Google CTF 2018/Shall We Play a Game?/images/fail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/Shall We Play a Game?/images/fail.jpg -------------------------------------------------------------------------------- /Google CTF 2018/Shall We Play a Game?/images/flag.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/Shall We Play a Game?/images/flag.jpg -------------------------------------------------------------------------------- /Google CTF 2018/Shall We Play a Game?/images/game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/Shall We Play a Game?/images/game.png -------------------------------------------------------------------------------- /Google CTF 2018/Shall We Play a Game?/images/mod.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/Shall We Play a Game?/images/mod.jpg -------------------------------------------------------------------------------- /Google CTF 2018/Shall We Play a Game?/images/smali.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/Shall We Play a Game?/images/smali.png -------------------------------------------------------------------------------- /Google CTF 2018/Shall We Play a Game?/modded.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/Google CTF 2018/Shall We Play a Game?/modded.apk -------------------------------------------------------------------------------- /RCTF/Number Game/README.md: -------------------------------------------------------------------------------- 1 | # Number Game - 206 Points 2 | 3 | ### Guess Guess Guess 4 | 5 | Server: nc 149.28.139.172 10002 6 | 7 | ## Solution: 8 | 9 | Script: [solve.py](./solve.py) 10 | 11 | Bruteforce the input using alphanumeric characters. Then play the old game of [Cows and Bulls](https://en.wikipedia.org/wiki/Bulls_and_Cows) 8 times to get the flag! 12 | 13 | The code to play the game was found here: [Cows and Bulls Solution](https://rosettacode.org/wiki/Bulls_and_cows/Player#Python) 14 | -------------------------------------------------------------------------------- /RCTF/Number Game/solve.py: -------------------------------------------------------------------------------- 1 | 2 | import itertools 3 | from pwn import remote 4 | from hashlib import sha256 5 | import string 6 | from itertools import permutations, izip 7 | from random import shuffle 8 | 9 | # helper function for the cows and bulls game 10 | def parse_score(score): 11 | score = score.strip().split(', ') 12 | return tuple(int(s.strip()) for s in score) 13 | 14 | # helper function for the cows and bulls game 15 | def scorecalc(guess, chosen): 16 | bulls = cows = 0 17 | for g,c in izip(guess, chosen): 18 | if g == c: 19 | bulls += 1 20 | elif g in chosen: 21 | cows += 1 22 | return bulls, cows 23 | 24 | # keep trying until you find the flag 25 | while True: 26 | try: 27 | sh = remote("149.28.139.172", 10002) 28 | data= sh.recvline()[:-1] 29 | print(data) 30 | text, hash = data.split(' == ') 31 | text = text[12:-1] 32 | alpha = string.uppercase + string.lowercase + string.digits 33 | print('Bruteforcing input...') 34 | pos = map(''.join, itertools.product(alpha, repeat=4)) 35 | 36 | # bruteforce all possibilities 37 | for x in pos: 38 | if sha256(x + text).hexdigest() == hash: 39 | print('Match found: sha256(%s) == %s' % (x+text, hash)) 40 | print(sh.recvuntil(':')) 41 | print(x) 42 | sh.send(x+'\n') 43 | break 44 | 45 | # start playing the cows and bulls game 46 | digits = '0123456789' 47 | size = 4 48 | 49 | for _ in range(8): 50 | print(sh.recvuntil('6 times\n')) 51 | choices = list(permutations(digits, size)) 52 | shuffle(choices) 53 | answers = [] 54 | scores = [] 55 | while True: 56 | ans = choices[0] 57 | answers.append(ans) 58 | print(' '.join(ans)) 59 | sh.send(' '.join(ans)+'\n') 60 | data = sh.recvline() 61 | print(data) 62 | if 'You got it!' in data: 63 | break 64 | score = data[-5:-1] 65 | score = parse_score(score) 66 | scores.append(score) 67 | found = score == (size, 0) 68 | choices = [c for c in choices if scorecalc(c, ans) == score] 69 | 70 | # get the flag 71 | print(sh.recv()) 72 | break 73 | except EOFError: 74 | print('\nFailed. Attempting again...\n') 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ctf-write-ups 2 | Capture The Flag Write-ups 3 | -------------------------------------------------------------------------------- /b00t2root '18/Teleport/README.md: -------------------------------------------------------------------------------- 1 | # Teleport - 200 2 | 3 | Here's a little fun exercise, see if you can find the flaw and pwn the service running at 159.89.167.13:1234 4 | 5 | [teleport.py](./teleport.py) 6 | 7 | Author: Dedsec 8 | 9 | 10 | ## Solution 11 | 12 | Script: [teleportSolve.py](./teleportSolve.py) 13 | 14 | The service appears to be a simple encryption with xor using a randomly generated key consisting of random integers from 0 to 255. However the key is never being printed or stored anywhere and appears to be lost after the encryption (or is it?). 15 | 16 | ### The Exploit 17 | For better understanding, consider the following snippet in python: 18 | ```py 19 | import numpy as np 20 | 21 | def send_data(x): 22 | # Initializing a numpy array 23 | np.array([int(x)]) 24 | 25 | def receive_data(): 26 | # Return an empty numpy array 27 | return np.empty((), dtype=np.int).tolist() 28 | ``` 29 | 30 | **Output** 31 | ```py 32 | >>> send_data(42) 33 | >>> receive_data() 34 | 42 35 | ``` 36 | 37 | Surprising, isn't it! How did the data teleport from one function to another without ever being returned anywhere or stored globally?! One would expect the array initialized in `send_data()` to be lost forever and an empty array containing garbage values to be returned by the `receive_data()`. Clearly that's not the case. So what gives? 38 | 39 | What is happening is that **we initialized an array in `send_data()` but never returned it** from the function so python marks it free for reallocation. Then when we call the `receive_data()` it looks for the **next memory location which is free and returns it without initializing**. Coincidentally it happens to be the same one that just got freed thus returning the array initialized in `send_data()`! 40 | 41 | ### Solution 42 | Now coming back to the question, we see that the `key` array used for encrypting the flag has been initialized in `encrypt()` but never returned. From the above example we know it isn't lost forever ;). Also we notice the `return np.empty((int(len(ctxt)/2)), dtype=np.int).tolist()` in `line 24`. So if we can somehow get to execute this line immedietly after encrypting the flag, we can recover the original key! 43 | 44 | If we analyze the code, we'll realize this line will be executed only when an exception is raised in `line 14` at `inp = input('Enter the key: ').split(' ')`. By the usage of `input()` it is obvious the script is running in python3 so we can raise a `UnicodeDecodeError` by inputting a non-unicode character (like `\x80`). This will trigger the `except` block thus executing the required line and causing the original `key` array to be returned. 45 | 46 | So we first execute `Get Flag` which returns a hex containing 64 characters implying the flag contains 32 characters *(as hex of 32 characters will have 64 characters)*. Then we execute `decrypt()` by inputting a dummy ciphertext of length 64. When it asks for the key, we input a non utf-8 character like `\x80` causing an exception thus making it return the `key`. 47 | Now we simply execute the `decrypt()` function again and input the actual ciphertext and the original key obtained above to get the flag: **`b00t2root{t3l3p0r7at10n_1s_r34l}`** 48 | 49 | *Note: You can't simply type `\x80` in the terminal as python would treat it as a string consisting of 4 characters* 50 | 51 |  52 | -------------------------------------------------------------------------------- /b00t2root '18/Teleport/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AdityaVallabh/ctf-write-ups/91ce9552768d8db44f9456311b3513490f60d3c6/b00t2root '18/Teleport/demo.png -------------------------------------------------------------------------------- /b00t2root '18/Teleport/teleport.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import random 3 | import codecs 4 | from SECRET import flag 5 | 6 | def encrypt(ptxt): 7 | message = np.array([int(ord(c)) for c in ptxt]) 8 | key = np.array(random.sample(range(0, 255), len(ptxt))) 9 | ctxt = (''.join([chr(a^b) for (a,b) in zip(message, key)])).encode('utf-8').hex() 10 | return ctxt 11 | 12 | def decrypt(ctxt): 13 | try: 14 | inp = input('Enter the key: ').split(' ') 15 | try: 16 | key = np.array(list(map(int, inp))) 17 | except: 18 | print('Key should be an array of integers!') 19 | return 20 | ctxt = np.array([ord(c) for c in codecs.decode(ctxt, 'hex').decode()]) 21 | ptxt = ''.join(np.array([chr(a^b) for (a,b) in zip(ctxt, key)])) 22 | return ptxt 23 | except: 24 | return np.empty((int(len(ctxt)/2)), dtype=np.int).tolist() 25 | 26 | print('Welcome to The Most Secure Encryption/Decryption Service') 27 | 28 | while True: 29 | print('1. Encrypt') 30 | print('2. Decrypt') 31 | print('3. Get Flag') 32 | choice = int(input(('What do you want to do: '))) 33 | 34 | if choice == 1: 35 | ptxt = input('Enter the message: ') 36 | ctxt = encrypt(ptxt) 37 | print('Ciphertext: ' + ctxt) 38 | elif choice == 2: 39 | ctxt = input('Enter the ciphertext: ') 40 | ptxt = decrypt(ctxt) 41 | print('Plaintext: ' + str(ptxt)) 42 | elif choice == 3: 43 | ctxt = encrypt(flag) 44 | print('Encrypted Flag: ' + ctxt) 45 | else: 46 | break 47 | -------------------------------------------------------------------------------- /b00t2root '18/Teleport/teleportSolve.py: -------------------------------------------------------------------------------- 1 | from pwn import * 2 | 3 | conn = remote('159.89.167.13', 1234) # connect to the service 4 | 5 | 6 | print conn.recvuntil('do:'), # execute Get Flag() 7 | print('3') 8 | conn.send('3\n') 9 | data = conn.recvline() 10 | print data, 11 | ctxt = data.split(' ')[3] # store the ciphertxt 12 | 13 | 14 | print conn.recvuntil('do:'), # execute Decrypt() 15 | print('2') 16 | conn.send('2\n') 17 | print conn.recvuntil('ciphertext:'), 18 | print('a'*64) 19 | conn.send('a'*64+'\n') # input dummy ciphertext 20 | print conn.recvuntil('key:'), 21 | print('\x80') 22 | conn.send('\x80\n') # send a non-unicode character to cause an exception in Decrypt() 23 | data = conn.recvuntil(']') 24 | print data, 25 | key = data[13:-1].replace(',','') # an empty array pointing to the actual key used during encryption of flag above is returned 26 | 27 | 28 | print conn.recvuntil('do:'), # execute Decrypt() 29 | print('2') 30 | conn.send('2\n') 31 | print conn.recvuntil('ciphertext:'), # enter the ciphertext obtained above 32 | print ctxt, 33 | conn.send(ctxt) 34 | print conn.recvuntil('key:'), # enter the key obtained above 35 | print(key) 36 | conn.send(key + '\n') 37 | 38 | print conn.recvline(), # get the Flag ;) 39 | conn.recv() 40 | conn.send('4\n') 41 | -------------------------------------------------------------------------------- /b00t2root '19/Xorxery/solve.py: -------------------------------------------------------------------------------- 1 | from pwn import remote, log 2 | import hashlib 3 | 4 | cmd = 'nc 127.0.0.1 3002' 5 | nc, host, port = cmd.split() 6 | sh = remote(host, port) 7 | 8 | def oracle(a, b): 9 | # import time; time.sleep(.5) 10 | oracle.counter += 1 11 | ha = '{:016x}'.format(a) 12 | hb = '{:016x}'.format(b) 13 | (sh.recvuntil('choice: ')), 14 | sh.sendline('2') 15 | (sh.recvuntil('Username: ')), 16 | sh.sendline(ha) 17 | (sh.recvuntil('Password: ')), 18 | sh.sendline(hb) 19 | data = sh.recvline() 20 | if 'username/password' in data: 21 | return 1 22 | elif 'password/username' in data: 23 | return -1 24 | return 0 25 | 26 | def solve(): 27 | a, b = 0, 0 28 | big = oracle(0,0); 29 | for i in range(63, -1, -1): 30 | f = oracle(a^(1< px: 24 | print('Incorrect username/password!') 25 | else: 26 | print('Incorrect password/username!') 27 | 28 | def get_flag(ctxt): 29 | key = input('Enter key: ') 30 | try: 31 | f = Fernet(key) 32 | ptxt = f.decrypt(ctxt) 33 | print(ptxt.decode()) 34 | except: 35 | print('Invalid key!') 36 | 37 | def init(): 38 | username = os.urandom(8) 39 | password = os.urandom(8) 40 | key = base64.b64encode(hashlib.md5((username + password)).hexdigest().encode()) 41 | f = Fernet(key) 42 | ctxt = f.encrypt(FLAG.encode()) 43 | print('Welcome to the service!') 44 | return username, password, ctxt 45 | 46 | def print_menu(): 47 | print('1. Get flag') 48 | print('2. Get key') 49 | print('3. Exit') 50 | choice = input('Enter your choice: ') 51 | return choice 52 | 53 | def main(): 54 | username, password, ctxt = init() 55 | counter = 0 56 | while counter < LIMIT: 57 | choice = print_menu() 58 | if choice == '1': 59 | get_flag(ctxt) 60 | elif choice == '2': 61 | get_key(username, password) 62 | else: 63 | break 64 | counter += 1 65 | if counter >= LIMIT: 66 | print('Max tries exceeded!') 67 | print('Exiting.') 68 | 69 | if __name__ == "__main__": 70 | main() --------------------------------------------------------------------------------