Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
GovanifY
ctf-re
Commits
130ff3b6
Commit
130ff3b6
authored
Apr 26, 2020
by
GovanifY
Browse files
chals done + bug fixing
parent
98fa7e9f
Changes
8
Hide whitespace changes
Inline
Side-by-side
README.md
View file @
130ff3b6
CTF Anti cheat
==========
How to setup:
*
put your challengess in chals folder. One folder per chal, each folder must contain
a setup.sh
This is a deterministic anti-cheat/unique binaries generator specially tailored
to CTFs. It creates a regex per challenge to load into your flag platform.
The flag itself is a sha2 of the challenge name plus a secret on top of 4 bytes
at a different position for each challenge identifying uniquely the team in a
sneaky fashion.
The setup also includes ways to add junk code to binaries and have more
convoluted setup done to them through a python script.
All the flags and binaries are deterministic, so as long as you don't re-roll
your secrets you will get the same result each time.
The cheat detection part detects those bytes to see who cheated with who,
probabilistically or definitely, or simply who cheated if the identifying bytes
cannot be found. The whole idea is to have hashes that looks the most alike
possible while still being fairly certain about the origin of said flag.
## Setup:
Put your challengess in the folder called "chals". One folder per challenge, each folder must contain
a setup.py setting up the challenge final folder and removing unecessary files.
You will end up with a folder tree such as:
```
chals_out/
├── access_security
│ ├── regex.txt
│ ├── test_team
│ │ ├── access_security
│ │ └── flag.txt
│ └── test_team2
│ ├── access_security
│ └── flag.txt
```
You can thus automate deployment of the binaries onto your VMs however you like.
This setup works well with binaries that won't be executed remotely too and whatnot.
challenges done:
*
reverse_rop
*
simple_rop
*
simple_rop_2
*
web_server
*
snake_oil
*
snake_oil_2
*
access_security
*
web_server_2
TODO:
*
modern_rop
## RE CTF Edition:
challenges list:
-
reverse_rop
-
simple_rop
-
simple_rop_2
-
web_server
-
snake_oil
-
snake_oil_2
-
access_security
-
web_server_2
-
modern_rop
chals/modern_rop/Makefile
View file @
130ff3b6
all
:
gcc
rop
.c
-o
modern_rop
gcc
main
.c
-o
modern_rop
strip modern_rop
chals/modern_rop/main.c
0 → 100644
View file @
130ff3b6
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdbool.h>
#include <stdint.h>
//--JUNK CODE--
//--JUNK CODE--
int
main
(
int
argc
,
const
char
**
argv
)
{
char
s
[
8
];
printf
(
"Qui êtes vous? "
);
fgets
(
s
,
16
,
stdin
);
long
buffer
[
80
];
for
(
int
i
=
0
;
i
<
80
;
i
++
){
buffer
[
i
]
=
0x4141414141414141
;
if
(
i
==
37
){
//--JUNK CODE--
//--JUNK CODE--
long
p_tgr
=
(
long
)
&
printf
;
//--JUNK CODE--
//--JUNK CODE--
buffer
[
i
]
=
p_tgr
;
//--JUNK CODE--
//--JUNK CODE--
}
}
printf
(
"Vous êtes donc: "
);
printf
(
s
,
16
);
printf
(
" !
\n
Quel est votre mot du jour? "
);
//--JUNK CODE--
//--JUNK CODE--
fgets
(
s
,
256
,
stdin
);
printf
(
"
\n
Ah, quel dommage :/"
);
return
0
;
}
chals/modern_rop/rop.c
deleted
100644 → 0
View file @
98fa7e9f
#include <stdio.h>
int
main
(
int
argc
,
const
char
**
argv
)
{
char
s
[
8
];
printf
(
"Enter name : "
);
fgets
(
s
,
16
,
stdin
);
puts
(
"Hello"
);
printf
(
s
,
16
);
printf
(
"Enter sentence : "
);
fgets
(
s
,
256
,
stdin
);
return
0
;
}
chals/modern_rop/setup.py
View file @
130ff3b6
# TODO
import
subprocess
import
os
import
sys
from
colorama
import
Fore
,
Back
,
Style
# chals_out/chal_name/team_name so 3
sys
.
path
.
insert
(
1
,
os
.
path
.
join
(
sys
.
path
[
0
],
'../../..'
))
from
libchals
import
*
from
pwn
import
*
context
.
log_level
=
'error'
FNULL
=
open
(
os
.
devnull
,
'w'
)
# junk code generation
write_junk_calls
(
"main.c"
,
41
,
3
)
write_junk_calls
(
"main.c"
,
28
,
3
)
write_junk_calls
(
"main.c"
,
24
)
write_junk_body
(
"main.c"
,
10
)
subprocess
.
call
(
"make"
,
stdout
=
FNULL
,
stderr
=
FNULL
)
# in a perfect world we would actually generate a correction for each challenge
# but I'm too lazy to auto-calculate a string format finder automatically.
# the idea is simply: print puts address, print canary, build ropchain to call
# system with printed canary and libc base thanks to puts. No need to even
# defeat PIE!
"""
# we generate the rop
elf = ELF("reverse_rop")
rop = ROP(elf)
FLAG1 = elf.symbols['flag1']
FLAG2 = elf.symbols['flag2']
FLAG3 = elf.symbols['flag3']
FLAG6 = elf.symbols['flag6']
XOR = elf.symbols['xor']
XOR2 = elf.symbols['xor2']
XOR3 = elf.symbols['xor3']
POP_ONCE = (rop.find_gadget(['pop ebx', 'ret']))[0]
padding = b'A' * 28
exploit = padding + p32(FLAG1) + p32(FLAG2) + p32(POP_ONCE) + p32(0xAABBCCD2)
exploit += p32(XOR) + p32(FLAG3) + p32(POP_ONCE) + p32(0xAABBCCD1) + p32(FLAG3)
exploit += p32(POP_ONCE) + p32(0xAABBCCD2) + p32(XOR2) + p32(FLAG3) +p32(POP_ONCE)
exploit += p32(0xAABBCCD5) + p32(XOR3) + p32(FLAG6) + p32(POP_ONCE) + p32(0xBBCCDDE9) + p32(0xBBCCDDE3)
# rop is saved as input
f = open("input", "wb")
f.write(exploit)
f.close()
# strip it after the ropchain building so they don't have the symbols but we do
subprocess.call(["strip", "reverse_rop"], stdout=FNULL, stderr=FNULL)
"""
os
.
remove
(
"main.c"
)
os
.
remove
(
"Makefile"
)
os
.
remove
(
"setup.py"
)
# see above
"""
# TESTING BINARY
try:
output = subprocess.check_output("gdb -ex 'run < input' -ex 'print $eip' -ex quit ./reverse_rop | tail -n 1", shell=True, stderr=subprocess.STDOUT)
except Exception as e:
output = str(e.output)
if not b"0xbbccdde3" in output:
fail_test()
"""
detect_cheat.py
View file @
130ff3b6
...
...
@@ -105,13 +105,13 @@ for i in chals:
regex_done
=
False
# we calculate the location of the identifying bytes
salt_chal
=
small_hash
(
i
)
salt_chal
=
small_hash
(
i
+
SECRET
)
salts
=
[]
for
z
in
range
(
0
,
4
):
salts
=
add_salt
(
salts
,
salt_chal
[
z
]
%
32
)
for
y
in
team_names
:
uni_hash
=
small_hash
(
i
+
y
)
uni_hash
=
small_hash
(
i
+
y
+
SECRET
)
# make it a bit harder to guess the format of the sha2
chal_name
=
i
+
SECRET
hash_final
=
bytearray
(
bytes
.
fromhex
(
hashlib
.
sha256
(
chal_name
.
encode
()).
hexdigest
()))
...
...
gen_chals.py
View file @
130ff3b6
...
...
@@ -95,14 +95,14 @@ for i in chals:
regex_done
=
False
# we calculate the location of the identifying bytes
salt_chal
=
small_hash
(
i
)
salt_chal
=
small_hash
(
i
+
SECRET
)
salts
=
[]
for
z
in
range
(
0
,
4
):
salts
=
add_salt
(
salts
,
salt_chal
[
z
]
%
32
)
for
y
in
team_names
:
copy_dir
(
"chals/"
+
i
,
"chals_out/"
+
i
+
"/"
+
y
)
uni_hash
=
small_hash
(
i
+
y
)
uni_hash
=
small_hash
(
i
+
y
+
SECRET
)
# make it a bit harder to guess the format of the sha2
chal_name
=
i
+
SECRET
hash_final
=
bytearray
(
bytes
.
fromhex
(
hashlib
.
sha256
(
chal_name
.
encode
()).
hexdigest
()))
...
...
libchals.py
View file @
130ff3b6
...
...
@@ -11,7 +11,7 @@ Some limitations:
then junk definition, from the bottom up.
"""
HASH_ROUND
=
-
1
HASH_ROUND
=
0
def
rng
(
index
):
global
HASH_ROUND
BUF_SIZE
=
65536
...
...
@@ -235,7 +235,6 @@ junk_min=0
def
write_junk_body
(
fd
,
line
):
global
junk_called
global
fun_names
global
HASH_ROUND
# junk generator!!
dont_gen_name
=
False
global
junk_min
...
...
@@ -244,10 +243,8 @@ def write_junk_body(fd, line):
junk_count
=
junk_min
if
(
fun_names
!=
[]):
dont_gen_name
=
True
else
:
HASH_ROUND
+=
1
for
i
in
range
(
0
,
junk_count
+
1
):
junk_to_add
=
rng
(
i
%
len
(
junk
)
)
%
len
(
junk
)
junk_to_add
=
rng
(
i
%
32
)
%
len
(
junk
)
# use this
if
(
not
dont_gen_name
):
fun_names
.
append
(
random_name
())
...
...
@@ -258,7 +255,6 @@ def write_junk_calls(fd, line, count=-1):
# junk generator!!
global
junk_called
global
fun_names
global
HASH_ROUND
global
junk_min
junk_count
=
rng
(
0
)
%
len
(
junk
)
if
(
junk_count
<=
junk_min
):
...
...
@@ -268,10 +264,11 @@ def write_junk_calls(fd, line, count=-1):
else
:
count
=
junk_called
+
junk_count
//
count
if
(
fun_names
==
[]
and
junk_called
==
0
):
HASH_ROUND
+=
1
gen_fun_names
()
if
(
count
>=
junk_count
):
count
=
junk_count
for
i
in
range
(
junk_called
,
count
):
junk_to_add
=
rng
(
i
%
len
(
junk
)
)
%
len
(
junk
)
junk_to_add
=
rng
(
i
%
32
)
%
len
(
junk
)
# use this
tmp
=
junk_calls
[
junk_to_add
].
replace
(
"FUNCTION_NAME"
,
fun_names
[
i
])
write_line
(
fd
,
line
,
tmp
.
replace
(
"VAR_NAME"
,
random_name
()))
...
...
@@ -284,14 +281,20 @@ def set_junk_min(m):
def
gen_fun_names
():
# junk generator!!
global
junk_min
global
fun_names
junk_count
=
rng
(
0
)
%
len
(
junk
)
if
(
junk_count
<=
junk_min
):
junk_count
=
junk_min
for
i
in
range
(
0
,
junk_count
+
1
):
junk_to_add
=
rng
(
i
%
len
(
junk
)
)
%
len
(
junk
)
junk_to_add
=
rng
(
i
%
32
)
%
len
(
junk
)
# use this
fun_names
.
append
(
random_name
())
def
increment_hash_round
():
# aka reset state
global
HASH_ROUND
global
fun_names
global
junk_called
fun_names
=
[]
junk_called
=
0
HASH_ROUND
+=
1
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment