Vielleicht praktisch für das nächste CTF: Quickemu, Quickly create and run optimised Windows, macOS and Linux desktop virtual machines.
Posts mit Tag "ctf"
Für ein CTF musste ich letztens einen Netfilter bauen, der ein- und ausgehende IP-Pakete patcht. Genauer gesagt mussten Daten in TCP-Header geändert werden (mehrere Reserved-Bits mussten gesetzt werden).
Nachdem ich hoffnungslos versucht hab, irgendwelche random TCP/IP-Userspace-Stacks mit Raw-Sockets oder TUN/TAP-Devices zum Laufen zu bringen, war die “einfache” Lösung ein Netfilter-Kernelmodul. Da man dazu immer mal wieder verschiedene Varianten findet und 90% der Tech-Blogs da draußen nicht in der Lage sind, Zeilenumbrüche und Quotes ordentlich zu formatieren, hier eine “einfache” Lösung (basiert teilweise auf dieser):
ultra_hack.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
MODULE_LICENSE("GPL-3.0");
MODULE_AUTHOR("Ultra Hacker 3000");
MODULE_DESCRIPTION("Super-TCP-Hack");
static unsigned int hook_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
if (!skb)
return NF_ACCEPT;
struct iphdr *iph = ip_hdr(skb);
if (iph->protocol != IPPROTO_TCP)
return NF_ACCEPT;
struct tcphdr *tcp_header = tcp_hdr(skb);
tcp_header->res1 = 0b1010;
printk(KERN_DEBUG "ultra hack is hacking.\n");
return NF_ACCEPT;
}
static struct nf_hook_ops nf_hook_data = {
.hook = hook_func,
.hooknum = NF_INET_POST_ROUTING,
.pf = PF_INET,
.priority = NF_IP_PRI_FIRST,
};
static int __init init_function(void)
{
printk(KERN_INFO "ultra hack module is loading.\n");
nf_register_net_hook(&init_net, &nf_hook_data);
return 0;
}
static void __exit exit_function(void)
{
printk(KERN_INFO "ultra hack module is unloading.\n");
nf_unregister_net_hook(&init_net, &nf_hook_data);
}
module_init(init_function);
module_exit(exit_function);
Ein paar Anmerkungen:
PF_INETsteht für IPv4- die
hooknumgibt die Stelle an, an der der Filter eingesetzt wird (das kann man noch Pre-Routing machen etc, einfach nach anderen Konstanten schauen). - Die Signatur der
hook_funcändert sich gerne mal bei Kernel-Updates. Die aus dem Beispiel funktioniert für5.11.0.
Hier noch die Makefile dazu:
obj-m := ultra_hack.o
LKM-objs += ultra_hack.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
rm -r -f *.mod.c .*.cmd *.symvers *.o
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean
Zum Laden, Entfernen etc:
# kompilieren
make all
# laden
sudo insmod ultra_hack.ko
# schauen, ob es da ist
lsmod
# schauen, ob printk-Nachrichten auftauchen
sudo dmesg -w
# entfernen
sudo rmmod ultra_hack
Man kann das auch mit nfqueue und Python machen.
BND-CTF: 86-GBE Message Service
Der nächste Writeup aus den BND-CTFs. Nach der Backdoor in der RSA-Schlüsselerzeugung, dieses Mal aus der Kategorie Binary Exploitation.
Das Ziel:
Exploit a binary to gain a remote shell.
Wir bekommen die server.c, sowie die auf dem Server genutzte libc.
Server-Code
{%- highlight c linenos -%} // gcc server.c -o server -g -ggdb -Wl,-z,norelro -fstack-protector-all#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h>
int my_read(FILE *fd, char *buf, size_t max) { int read = 0; while (read < max && !feof(fd)) { int ch = getc(fd); if (ch == ‘\n’) { break; } else { buf[read] = ch; read++; } } buf[read] = 0;
return read;
}
char rot13(char ch) { if (ch >= ‘a’ && ch <= ‘z’) { return ((ch - ‘a’ + 13) % 26) + ‘a’; } else if (ch >= ‘A’ && ch <= ‘Z’) { return ((ch - ‘A’ + 13) % 26) + ‘A’; } else if (ch >= ‘0’ && ch <= ‘9’) { return ((ch - ‘0’ + 5) % 10) + ‘0’; } else { return ch; } }
int main(int argc, char** argv) { setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0);
char inp[0x100];
char cc;
puts("Welcome to our 86-GBE service!");
puts("Enter your message and press return.");
puts("Enter quit to exit.");
unsigned char len;
for (;;) {
len = my_read(stdin, inp, sizeof (inp) - 1);
if (strcmp(inp, "quit") == 0) {
exit(0);
}
for (int i = 0; i < len; i++) {
inp[i] = rot13(inp[i]);
}
for (int i = 0; i < len / 2; i++) {
cc = inp[i];
inp[i] = inp[len -i -1];
inp[len - i - 1] = cc;
}
printf(inp);
putc('\n', stdout);
}
exit(0);
} {%- endhighlight -%}
Nehmt Euch am Besten etwas Zeit, um den Code erstmal anzuschauen.
Falls Ihr das nachstellen wollt, die Sha1-Hashes der beiden Shared-Objects und der bereitgestellten server-Executable sind:
BND-CTF: RSA Key Generation
Der BND hat mal wieder einen öffentlichen Einstellungstest, dem wir uns erfreuen dürfen. Ideal, um sich mal etwas in CTF-Writeups zu probieren!
Aus der Kategorie Cryptography schauen wir uns mal die (einzige) Challenge an: Analysieren einer Backdoor in einem RSA-Schlüsselerzeugungsalgorithmus.
Folgender Code wurde uns bereitgestellt:
SageMath-Code
{%- highlight c linenos -%} ### Excerpt from utils.py (used to encode / decode the message) def encode(msg): """ Convert a message to a number.Example:
>>> hex(encode(b'ABCD'))
'0x41424344'
"""
return int.from_bytes(msg, byteorder='big')
def decode(msg): """ Convert a number back to a message.
Example:
>>> decode(0x41424344)
b'ABCD'
"""
return int(msg).to_bytes(length=len(hex(msg))//2-1, byteorder='big')
Excerpt from RSA.py
x = 0xbb31781a2436fd6833597b61f91b94fba8cc5be702c7084de28625d96823102daf48dd84244fe41d180452a900388d1666ff59981f0912c6640977684c20bcfdcbf365dfcb68c0c5a9fd02576134a0e94ab9e20bbacffb4df5c9c27ae7f5022f6609aefeb9f5249387925ad13ce80a13
def rsa_keygen(): def a(x, y): z = 1 while True: if is_prime(xz + y): return xz + y z += 1
p = a(x, random_prime(2^128, None, 2^127))
q = a(x, random_prime(2^128, None, 2^127))
N = p * q
e = 65537
d = inverse_mod(e, (p-1) * (q-1))
return e, d, N
def rsa_crypt(m, k, n): return lift(Mod(m, n) ^ k) {%- endhighlight -%}
(wer kein SageMath installiert hat, kann diese Online-Version verwenden)
Zu dem Code haben wir folgende Zahlen enthalten, die wir entschlüsseln sollen: