At first i tried to factor N which give no promising result, after that @peter noticed the way server read filename can lead to buffer overflow that we can modify 2 bytes of e, after that we can re-read the flag file which will be encrypt using new e (let’s call it e'), if gcd(e, e')=1 we can recover the original message by extended gcd:

The multiplication modulo operation can be reversed easily using exgcd, remain step to reverse is the xor with mem4 and mem5 variables, but luckily mem[0]^mem[2] is constant after that step, so we can reverse blocks directly:

First, we obtain the encrypted welcome message which provides us full information of that block’s encryption:

Message welcome_txt: "Welcome!!" + "\x07"*7

IV welcome_iv: first 16 bytes of E_welcome

Ciphertext welcome_cip: remain 16 bytes of E_welcome

With these we can fully control the decrypted text of first block:

or

where

So to let the decrypt function to decrypt the first block into any plaintext just use IV xor P_0 xor P_target as the new IV.

So we can manage to run get-flag by that method and obtain the encrypted flag, which turned out to have 3 blocks.

The first block of flag starts with hitcon{ from the assert part in server code, which allow us to control first 7 bytes of the decrypted, which’s enough for the get-md5 command.

So for the first block, we can find each byte from 8th character by enumerating its value. Assume we already found out a prefix p, we can use the known welcome encryption information to generate encrypted md5 hash of p+c for any char c, and then find c_flag by looking up p+c_flag using that encrypted md5 lookup table. But at first we have to find out the 16th character of the first block so we can control the padding, which’s quite easy after we have all encrypted md5 hash of any single byte character S1: just enumerating the last byte c until we receive a hash that’s inside S1, at that time the decrypted block must be "get-md5" + (1 char) + (7 chars) + char(8), so the last byte is c xor 8.

For the second and third block, i used the part msg = recv_msg().strip() to solve each byte using md5 by controlling the padding value: if hash(s)=hash(s+c) then c must be a whitespace character.

This’s a Rock-paper-scissors game which use 0,1,2 as the move values. It first require us to beat Slime bot and 10 Alpaca bots to obtain the first flag, and then 10 Nozomi and 100 randomly selected bots to obtain the second flag. We must send our AI as lua code once and every match will run without additional input.

All bot use pseudo random number generator to generate their moves, send last move to our ai and read our next move, then compare these moves to decide who wins, we will play 100 000 matches each game and required to win 90%, which’s 90 000 matches in order to win the game.

Alpaca bot: this bot generate next move by using state transform formula number = last_user_move + number - 0x61C88647; which number is unsigned int and first generated using a true random generator, and then calculate its move as number mod 3.

Nozomi bot: this’s a more advanced bot which at first frightened me from understanding its state transform function (below), but after some google it’s turned out to be Park and Miller #2 RNG.

First we need a way to detect the current bot we’re facing with, the easiest way is using game-counter variable but will lead into problem with 100 randomized game. We need to win 90 000 games, which mean we can lose 10 000 games, that’s a lot, so i spend 30 first moves to decide which’s the bot we’re facing:

Slime bot: Its formula is easy to verify, if all the first 30 moves are in the pattern 0,1,2,0,1,2,0,1,2… then that’s it.

Alpaca bot: At first i think this bot’s so easy, as 0x61C88647 mod 3 = 1 we just need to keep sending 1 as our move for the first 30 moves and then the number variable wont be changed. I was wrong. But luckily checking for number of unchanged moves is enough to verify this bot, which 50% of the moves must not be changed if we keep sending move 1.

Nozomi bot: Simple, if it isnt slime, and isnt alpaca, then that’s it.

Now’s the time for “AI”:

Slime bot: bot’s next move is (last_move+1)%3, easy.

Alpaca bot: due to the overflow problem, i thought this’s a RE problem first and move to other problems, but then @trichimtrich found out the problem. Subtracting number by 0x61C88647 frequency overflow the operation, which add into it a factor of 2^32 mod 3 = 1, he completed solution for this bot and i ported to lua. Primary idea is that each time our predict move is different from the actual move, number must have been overflowed, we just need to keep a range that move the same speed as number to check for overflow and adjust the modulo value when our current predicted value is out or in of that range.

Nozomi bot: as the function is a PRNG which distributes sample equally within [0, 0x7FFFFFFF], we can somehow precompute a table of 2 000 000 samples which can be checked easily when number go into that table. I decided to use last 20 moves encoded in base 3 as key for the table. The probability that we failed to catch this table after 7000 moves is((0x7FFFFFFF-2000000)/0x7FFFFFFF)^7000 = 0.00147029, so our success rate is above 99%.

So this problem is faking DNS and *bypass* the dnssec checker function, after a while trying to crack that public key, i found out that the source contain .git, checkout the branch eddb23375ea4e08c67a63088ea08b4d5fc18a406 give us the private keys and that’s enough to build a dns server for this problem.

So javascript: scheme urls will be black out, but we can easily bypass that by using carriage return character "javascript\r:document.location.href=(...)+document.cookie", and then just wait for the flag, we can input that carriage return by running command in console:

For each new session, the server create a new random message, allow us to decodeMAX_TRIES message with provided crypt function and then filter only base64 compatible characters into a new string, remove the last character if that string length divide 4 remain 1, and finally base64-decode that string.

We can easily findout the secret message by enumerating each byte, except for byte 0 and 4 we need to enumerate 2 bytes, but only enumerating the 4 MSBs are enough, so 1 + 256*2 + 16*6 = 609 tries in worst case, which’s very enough.

Hey there fellow lizard how nice of you to drop by! Did you know those filthy humans really think that some numbers have special meanings? Seven, 13 and for some strange reason even 9000. Go and show them that a good prime does not make a secure cryptosystem!

flag{If you whistle while you’re pissing, you have two minds, where one is quite sufficient. If you have two minds, you are at war with yourself. If you are at war with yourself, it is easy for an external force to defeat you. This is why Mong-tse wrote. ‘A man must destroy himself before others can destroy him.’ | Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamaco laboris nisi …}

The Flat Earth Society has a secret information storage. There is enough evidence in it, to convince every member of them, that the earth is round. Unfortunately, we don’t have the key …
nc flatearth.fluxfingers.net 1721

The Professor on Flat Earth is Prof. Flux Horst. Only for bragging he made his own Blog showcasing his favorite Papers. Beeing the only Professor, he thinks he is the smartest Person arround the Plate. Can you proof him wrong and log into his Admin-Panel?Link

robots.txt provides us:

User-agent: *
Disallow: /login.php
Disallow: /admin.php

The login.php source contain:

<!-- TODO: Remove ?debug-Parameter! -->

So let’s try appending ?debug to that file, which show us the source code:

Maya society was broken into a class structure with four main levels: the nobility (Maya almehenob), the priesthood (Maya ahkinob), the common people (Maya ah chembal uinieol), and the slaves (Maya ppencatob). The most powerful of the ruling elite was known as the halach uinic. The halach uinic are alive and still secretly hold meetings today. You have to reverse engineer their communication and infiltrate the ruling elite. Link

One more PRNG problem. PHP normally use glibc’s random function as its underlying implementation, which is x[n]=(x[n-3]+x[n-31])>>1. My solution was generating all 2^32 possible cases and confirm with the md5 value. First part of the solution was the cache generator:

People’s Square (A.K.A. shenmhin guangshan in Shanghai Dialect) is a large public square in the Huangpu District of Shanghai, China.
We know Talent Yang is the king of People's Square. Now he provides you a strange guessing game, and he also demonstrates his talent by giving you the result of how he tackles this task. Can you show your talent to decrypt the secret?

After a while reading the binaries with IDA, it’s confirmed that this’s a problem about recovering a cipher text with known plain-cipher pairs on reduced version of AES (4 rounds). The given binary’s decompiled code looks like below:

So we have 1024 known cipher texts, in which there is 512 plain text in the format of 00 00 00 00 00 00 00 00 xx xx xx xx yy yy yy yy where xxxxxxxx is an integer from with values in range [0, 1024) and yyyyyyyy is a unix timestamp, and the rest is in the format of 01 01 01 01 01 01 01 01 xx xx xx xx yy yy yy yy.

So the final solution is solving the 4-th subkey of given AES session byte by byte, trying all 256 values for each byte of the subkey, perform a single aes decryption step each and xor all the values, the corresponding bytes in the received values will have xor-sum of zero with wrong probality of 2-8. The implementation for subkey recovery looks like below:

There is a rumor that Arsenal F.C., Talent Yang’s favourite team, always likes to set some “mission impossible” tasks for themselves.
For instance, with a 0-2 home defeat against FC Barcelona they must play a great game in Camp Nou to get to the next round of UEFA Champions League. Can they fulfil this task? Decrypt the provided ciphertext to help them!

(The executable of this challenge is similar to that of People's Square , check the slight difference and enjoy solving it)

Hint: You should perform 2^32 single round decryption first

This’s the same as people_square, but with only 126 known plain texts, with 63 in each group. The zero sumed xor trick cannot be used anymore…

I did not have enough time to solve this problem within the context, i was only 2 hours left and i was so tired due to almost 40 hours playing on both 0ctf and codegate.

The idea is that the internal state of aes before MixColumn step of 3rd round will have unique values when plain text has exactly 1 active byte. We can find out the final subkey by groups of 4 bytes each, confirm the validity of that group with that statement with wrong probability of 256!/[(256-63)! * 256^63] which is almost zero – more than good enough for us to confirm the validity of specified group.

Problem is that pure-python implement cant work anymore The implement i made for people_square require about 5 mins to check 10000 cases of a group, that will result in about 1491.30808889 days running (lol), so i decided to try out the AES Instruction Set (which’s used in the problem statement too), and surprisingly, the problem is solved after 20 mins running time!!!

We’re trying a new mac here at BKP—HMAC-CRC. The hmac (with our key) of “zupe zecret” is ‘0xa57d43a032feb286’. What’s the hmac of “BKPCTF”? https://s3.amazonaws.com/bostonkeyparty/2016/0c7433675c3c555afb77271d6a549bf5d941d2ab

You are trying to sneak into a network, and and impersonate target computer.

Here, you would connect to computers connected via network, and ‘impersonate the computer by exchanging IP address’.

In the target’s network, computers are connected as illustrated below:

You are trying to impersonate computer A by accessing it from computer F.

In here, you can impersonate A by accessing it in the sequence of B -> D -> A, but the IP addresses of computer B and D will be changed.

However, if you access computer A in the order described below, you can impersonate without changing the IP addresses of computers other than A and F.

C -> E -> A -> D -> B -> E -> C -> F -> B -> D -> A

Please sneak into the network as illustrated below, and impersonate computer A from computer P.

Please specify and generate the shortest path as an output.

(In the example above, you may generate the output ‘CEADBECFBDA’.)

If you encounter multiple paths with the same amount of steps, please generate the first value in alphabetical order as the output.

Please submit TMCTF{<Your output>} as your answer.

Solution

I solved this problem using 2-ends BFS (yes, i have quite enough RAM for it:P). A state for this problem represented by the permutation of the assigned IP address of the machines and the last machine to move on. As we will have at most 16 values for 16 machines, we can use 64-bit unsigned int to represent the permutation state, and another 4-bit for last-machine, so uint128 is enough, but finally i was too lazy to do all bitwise operations and use pair struct :D.

64-bit collision approach

It’s a web service which provide key-value storage functions via JSON requests, keys are stored using its masked hash value using python’s built in hash function. So I decided it a collision-finding problem. At first, looking at this line:

assert (hash('PPP') != 2149279368079130035)

Which made me thought that python is running using version 3.4 with new SIPHASH24 hash algorithm and SPENT A DAY TO CRACK SIPHASH without success… (poor me :(). When i could manage to talk with w~ about this problem, i realized that it’s only the old hash function with randomized secret keys.

A quick search on python source code gave me the implementation of its string hashing:

Quite simple huh, start with an const, mul and or rounds, and ending by xor with its length and another const. Problem was that we dont know the value of _Py_HashSecret.prefix and _Py_HashSecret.suffix, let’s call them A and B from now on. As the calculation is based on simple operations only, we can use tools like z3 to solve the SMT model, but due to laggy internet, I decided to leave that path. Let’s calculate some simple hashes:

Now how can we sove that for A? As its type is long on 64 bits machine, we can’t do a full bruteforce search.

There’s an interesting feature of multiplication and xor is that the suffix of result if equal to operation result of suffixes of its operants. i.e. 0x12130x3456 = 0x3B1EE62 while 0x130x56 = 0x662, both share the same byte suffix. Using this we can solve for its value byte-byte-byte.

Using this method, i can (always) obtain 2 possible values pair for A and B, so i decided to use only the first one.

From this point, A and B are known, so we can simulate the hashing function locally. Now we must find collision for string “you_want_it_LOLOLOL?”.

The hashing value is 64 bits, so we have to search on a 64-bit space to obtain the collision, after some simple test on small strings, i think this hashing is nearly unique for them, which lead me to consider only in 8-byte strings. Thinking the hash function as a finity state graph, starting at A, we can go to next hash number using current character of input string. A full BFS rooting from A will require 2^64 nodes to be visited, which is impossible for our normal computer. We can try BFS from both side: the source node and the target node, and check if they can visit a common node, but this approach is for 128GB computer only – as we have to save hash (8 byte each) of 2^32 values, which require approx. 34GB RAM.

My last step to solve this problem is optimizing this: BFS only 3 bytes from each node, and try to do something for the remain 2 bytes (8-3*2 bytes). The first approach should be 256^2 loop:

for char1 in range(256):
for char2 in range(256):
TWO_WAY_BFS()

which should require some hours to finish. Looking more deeply on the hashing algorithm bring me an idea:

the assigment x = (1000003*x) ^ *p++; which our controlled p can assign any value to the LSByte of x!

So a byte can be obmitted from the search because it can be calculated using next and previous values, which reduce the searching space to 7 bytes, the obmitted byte will be the glue of 2 BFSs.

Finally making it run 8 processes in parallel and we got the flag flag{wh0_n3edz22Z22zZ_p3p456}.

> airdecap-ng -w A4:3D:F6:F3:74 file.pcap
Total number of packets read 45169
Total number of WEP data packets 15477
Total number of WPA data packets 0
Number of plaintext data packets 0
Number of decrypted WEP packets 15477
Number of corrupted WEP packets 0
Number of decrypted WPA packets 0

The passphrase length is longer than 6, so from this i can guess the passphrase length is 10 (as passhrase is ascii, we just need to look at msb of the result to guess). I was feeling stupid to see how long can my original bruteforce script need to brute these 10 characters.

Let’s rewrite the relations in more readable form:

p[0] xor p[4] xor p[8] = 0x6b

p[1] xor p[5] xor p[9] = 0x76

p[2] xor p[6] = 0x12

Using these relations, the key space is still 7 characters, cracking that for charset of 52 values should take hours, so i tried to install cuda toolkit to use gpu, but my crappy internet dont allow me to do so. From the relation, in both set {p[0], p[4], p[8]}, {p[1], p[5], p[9]}, there must be 2 uppercase letters or no letter at all for each set, so I guessed that all characters are lowercase. Now let’s run a bruteforce script for lowercase charset: