1000xREV / bit-by-bit
This program is taking forever to check my flag! It's been running for over 24 hours, and it's still not done. Can you take a look?
If we run the program and input a flag, sometimes we get a response that the flag is invalid, other times the program just hangs and prints .
:
The program seems to call some functions related to DNS:
The values passed to the functions are pretty obfuscated, but we can use dynamic analysis.
It seems to get the IP address of a nameserver. It sets some value on the result of __res_state
, and, with a bit of research, we find out that it’s setting the nameserver to query for further DNS records.
On the next call to res_query
we find it’s querying the domain len.rev.lac.tf
. Then it checks if the length of the flag is equal to the record data:
By setting a breakpoint at the cmp
instruction, we find that the expected length is 37.
Then it starts querying domains, starting with 0.rev.lac.tf
. It expects a response of type A;B,C
:
- A: The next subdomain to query
- B: The bit index in the flag
- C: Whether the bit should be set or not
B and C are optional. In that case, it doesn’t check anything, it just queries the next subdomain. The reason the program is taking so long to check the flag is because there are thousands of subdomains in this nameserver.
However, we can perform a zone transfer attack. This will list all subdomains in the nameserver and we can process them locally. This is done by calling dig @34.169.35.135 rev.lac.tf axfr | tee dns1.txt
.
Then we need to process the data from dig
, by following the subdomains starting with 0 and printing when it finds a value:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
with open("dns1.txt") as file:
records = file.readlines()
records = [record.split() for record in records if record.strip() != "" and not record.startswith(";")]
records = {record[0].split(".")[0]: record[4].strip('"') for record in records if record[3] == "TXT" and ";" in record[4]}
records = {key: value.split(";") for key, value in records.items()}
current = "0"
while True:
if current not in records:
break
if records[current][1] != "":
print(records[current][1])
current = records[current][0]
Now we have an entry for every bit in the flag, and whether it should be set or not. We can restore the flag:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
with open("actual.txt") as file:
records = file.readlines()
records = [[int(item) for item in record.strip().split(",")] for record in records]
def bt(value, bit_index):
return (value >> bit_index) & 1
flag = [0 for _ in range(37)]
for record in records:
should_set_bit = record[1]
index = record[0]
if should_set_bit:
flag[index >> 3] |= (1 << (~index & 7))
print(flag)
flag = [chr(char) for char in flag]
print("".join(flag))