Browse Source

chals done + bug fixing

master
parent
commit
130ff3b6bf
8 changed files with 175 additions and 43 deletions
  1. +41
    -15
      README.md
  2. +1
    -1
      chals/modern_rop/Makefile
  3. +46
    -0
      chals/modern_rop/main.c
  4. +0
    -13
      chals/modern_rop/rop.c
  5. +71
    -1
      chals/modern_rop/setup.py
  6. +2
    -2
      detect_cheat.py
  7. +2
    -2
      gen_chals.py
  8. +12
    -9
      libchals.py

+ 41
- 15
README.md View File

@ -1,20 +1,46 @@
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

+ 1
- 1
chals/modern_rop/Makefile View File

@ -1,3 +1,3 @@
all:
gcc rop.c -o modern_rop
gcc main.c -o modern_rop
strip modern_rop

+ 46
- 0
chals/modern_rop/main.c View File

@ -0,0 +1,46 @@
#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(" !\nQuel est votre mot du jour? ");
//--JUNK CODE--
//--JUNK CODE--
fgets(s, 256, stdin);
printf("\nAh, quel dommage :/");
return 0;
}

+ 0
- 13
chals/modern_rop/rop.c View File

@ -1,13 +0,0 @@
#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;
}

+ 71
- 1
chals/modern_rop/setup.py View File

@ -1 +1,71 @@
# 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()
"""

+ 2
- 2
detect_cheat.py View File

@ -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()))


+ 2
- 2
gen_chals.py View File

@ -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()))


+ 12
- 9
libchals.py View File

@ -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

Loading…
Cancel
Save