Cybersecurity Notes
MathematicsCryptography
  • Cybersecurity Notes
  • Binary Exploitation
    • Stack
      • Introduction
      • ret2win
      • De Bruijn Sequences
      • Shellcode
      • NOPs
      • 32- vs 64-bit
      • No eXecute
      • Return-Oriented Programming
        • Calling Conventions
        • Gadgets
        • Exploiting Calling Conventions
        • ret2libc
        • Stack Alignment
      • Format String Bug
      • Stack Canaries
      • PIE
        • Pwntools, PIE and ROP
        • PIE Bypass with Given Leak
        • PIE Bypass
      • ASLR
        • ASLR Bypass with Given Leak
        • PLT and GOT
        • ret2plt ASLR bypass
      • GOT Overwrite
        • Exploiting a GOT overwrite
      • RELRO
      • Reliable Shellcode
        • ROP and Shellcode
        • Using RSP
        • ret2reg
          • Using ret2reg
      • One Gadgets and Malloc Hook
      • Syscalls
        • Exploitation with Syscalls
        • Sigreturn-Oriented Programming (SROP)
          • Using SROP
      • ret2dlresolve
        • Exploitation
      • ret2csu
        • Exploitation
        • CSU Hardening
      • Exploiting over Sockets
        • Exploit
        • Socat
      • Forking Processes
      • Stack Pivoting
        • Exploitation
          • pop rsp
          • leave
    • Heap
      • Introduction to the Heap
      • Chunks
      • Freeing Chunks and the Bins
        • Operations of the Fastbin
        • Operations of the Other Bins
      • Malloc State
      • malloc_consolidate()
      • Heap Overflow
        • heap0
        • heap1
      • Use-After-Free
      • Double-Free
        • Double-Free Protections
        • Double-Free Exploit
      • Unlink Exploit
      • The Tcache
        • Tcache: calloc()
        • Tcache Poisoning
      • Tcache Keys
      • Safe Linking
    • Kernel
      • Introduction
      • Writing a Char Module
        • An Interactive Char Driver
        • Interactivity with IOCTL
      • A Basic Kernel Interaction Challenge
      • Compiling, Customising and booting the Kernel
      • Double-Fetch
        • Double-Fetch without Sleep
      • The Ultimate Aim of Kernel Exploitation - Process Credentials
      • Kernel ROP - ret2usr
      • Debugging a Kernel Module
      • SMEP
        • Kernel ROP - Disabling SMEP
        • Kernel ROP - Privilege Escalation in Kernel Space
      • SMAP
      • modprobe_path
      • KASLR
      • KPTI
    • Browser Exploitation
      • *CTF 2019 - oob-v8
        • The Challenge
      • picoCTF 2021 - Kit Engine
      • picoCTF 2021 - Download Horsepower
  • Reverse Engineering
    • Strings in C++
    • C++ Decompilation Tricks
    • Reverse Engineering ARM
  • Blockchain
    • An Introduction to Blockchain
  • Smart Contracts and Solidity
  • Hosting a Testnet and Deploying a Contract
  • Interacting with Python
  • Writeups
    • Hack The Box
      • Linux Machines
        • Easy
          • Traceback
        • Medium
          • Magic
          • UpDown
        • Hard
          • Intense
      • Challenges
        • Web
          • Looking Glass
          • Sanitize
          • Baby Auth
          • Baby Website Rick
        • Pwn
          • Dream Diary: Chapter 1
            • Unlink Exploit
            • Chunk Overlap
          • Ropme
    • picoGym
      • Cryptography
        • Mod 26
        • Mind Your Ps and Qs
        • Easy Peasy
        • The Numbers
        • New Caesar
        • Mini RSA
        • Dachshund Attacks
        • No Padding, No Problem
        • Easy1
        • 13
        • Caesar
        • Pixelated
        • Basic-Mod1
        • Basic-Mod2
        • Credstuff
        • morse-code
        • rail-fence
        • Substitution0
        • Substitution1
        • Substitution2
        • Transposition-Trial
        • Vigenere
        • HideToSee
    • CTFs
      • Fword CTF 2020
        • Binary Exploitation
          • Molotov
        • Reversing
          • XO
      • X-MAS CTF 2020
        • Pwn
          • Do I Know You?
          • Naughty
        • Web
          • PHP Master
      • HTB CyberSanta 2021
        • Crypto
          • Common Mistake
          • Missing Reindeer
          • Xmas Spirit
          • Meet Me Halfway
  • Miscellaneous
    • pwntools
      • Introduction
      • Processes and Communication
      • Logging and Context
      • Packing
      • ELF
      • ROP
    • scanf Bypasses
    • Challenges in Containers
    • Using Z3
    • Cross-Compiling for arm32
Powered by GitBook
On this page
  • Overview
  • Disassembly
  • Cleaning Up
  • Working out the Flaw
  • Using the Flaw
  • Exploit
  • Local
  • Remote

Was this helpful?

Export as PDF
  1. Writeups
  2. CTFs
  3. Fword CTF 2020
  4. Reversing

XO

Messing with the XOR

Overview

Let's try running the file:

$ ./task 
Error while opening the file. Contact an admin!
: No such file or directory

Perhaps it wants a flag.txt file? Let's create one with the words FwordCTF{flag_flaggety_flag}:

$ ./task 

input : 
test
4
input : 
pao
2

This isn't quite counting the number of letters we enter. Let's see if the disassembly can shed any light on it.

Disassembly

First thing we notice is that every libc function is built into the binary due to the stripped names. We can confirm this with rabin2:

$ rabin2 -I task
[...]
static  true
[...]

Cleaning Up

Many of the functions can be handled using the return address and the general context. Some of the decompilation - especially the references to strings - may not have loaded in yet; make sure GHidra finishes analysing. We don't even need the exact C names, as long as we get the general gist it's all fine.

void main(void)
{
  int min_length;
  void *flag;
  void *input;
  long lVar1;
  long **xored;
  ulong flag_length;
  ulong input_length;
  long **in_RCX;
  long **extraout_RDX;
  long **output;
  ulong in_R8;
  long *in_R9;
  int i;
  
  FUN_00400b7d();
  
  flag = malloc(0x32);
  input = malloc(0x32);
  lVar1 = read("flag.txt",&DAT_004ac8e8);
  if (lVar1 == 0) {
    puts("Error while opening the file. Contact an admin!\n");
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  output = (long **)&DAT_004ac929;
  FUN_0040fd20(lVar1,&DAT_004ac929,flag);
  do {
    xored = (long **)malloc(0x32);
    FUN_00410cf0("input : ",output);
    scanf("%s");
    flag_length = strlen(flag);
    input_length = strlen(input);
    if (input_length < flag_length) {
      min_length = strlen(input);
    }
    else {
      min_length = strlen(flag);
    }
    i = 0;
    while (i < min_length) {
      in_RCX = (long **)(ulong)*(byte *)((long)input + (long)i);
      *(byte *)((long)xored + (long)i) =
           *(byte *)((long)flag + (long)i) ^ *(byte *)((long)input + (long)i);
      i = i + 1;
    }
    output = (long **)strlen(xored);
    FUN_0040f840(&DAT_004ac935);
    FUN_00420ab0(xored,output,extraout_RDX,in_RCX,in_R8,in_R9);
  } while( true );
}

The python equivalent of this is roughly

value = ''

for x, y in zip(flag, input):
  value += chr(ord(x) ^ ord(y))

print(len(garbage))

In short, it's an XOR function.

Working out the Flaw

To calculate the length of the string it uses

output = (long **)strlen(xored);

The key here is strlen stops at a null byte. If you input a character with the same value as the flag character in that position, it will XOR to become \x00.

Using the Flaw

We can test every possible character. If the returned value is one less than the length of the string, the last character is correct as it XORed to create a null byte.

To test different offsets we can pad using a value definitely not in the flag, such as #.

Exploit

Local

from pwn import *
from string import printable

p = process('./task')

known = ''

while True:
    for char in printable:
        p.recvline()
        p.sendline('#' * len(known) + char)     # '#' won't be in it, so any null byte is definitely the char we test

        resp = int(p.recvline().strip())

        if resp == len(known):                  # if it's the same length as the known, then the char we sent XORed to a null byte
            log.info(f'Character is {char}')
            known += char                       # append it to what we know

            if char == '}':                     # if '}', probably the end of the flag - print and exit
                log.success(f'Flag: {known}')
                exit(0)
            
            break                               # we know the char, we can exit the for loop and run it again with a different known length

Now we can just switch out the process type on the remote server.

Remote

from pwn import *
from string import printable

if args.REMOTE:
    p = remote('xo.fword.wtf', 5554)
else:
    p = process('./task')

known = ''

while True:
    for char in printable:
        p.recvline()
        p.sendline('#' * len(known) + char)     # '#' won't be in it, so any null byte are definitely the char we test

        resp = int(p.recvline().strip())

        if resp == len(known):                  # if it's the same length as the known, then the char we sent XORed to a null byte
            log.info(f'Character is {char}')
            known += char                       # append it to what we know

            if char == '}':                     # if '}', probably the end of the flag - print and exit
                log.success(f'Flag: {known}')
                exit(0)
            
            break                               # we know the char, we can exit the for loop and run it again with a different known length

Flag: NuL1_Byt35?15_IT_the_END?Why_i_c4nT_h4ndl3_That!

Last updated 4 months ago

Was this helpful?