Casual McDonald's Employee Scriptorium
BlogMemesGitHubAbout
  • root@JesusCries
  • ⛩️Red Teaming
    • Methodology
    • Red Team Infrastructure
    • Initial Access
    • Reconnaissance
    • Lateral Movement
    • Post-Exploitation
      • Credentials Dumping
    • Persistence
    • Evasion
      • Memory Scanner
      • Antimalware Scan Interface (AMSI)
      • Event Tracing for Windows (ETW)
      • Attack Surface Reduction (ASR)
      • Microsoft Windows Defender Application Control (WDAC)
      • EDR Evasion
    • Offensive Development
      • Process Injection & Shellcode Loader
      • Portable Executable (PE) Loader
      • User Defined Reflective Loader
      • Beacon Object Files
    • Command & Control (C2)
      • Cobalt Strike
      • Havoc
      • Mythic
      • Sliver
    • Miscellaneous
      • Interesting Read
      • Certification Reviews
        • Certified Red Team Lead (CRTL)
  • 🧊Active Directory & Pentest
    • Check List
  • 🚩CTF Writeups
    • Reverse Engineering
      • Wargames.MY 2024: World 3
      • Wargames.MY 2023: Defeat the boss!
      • ACS 2023: Licrackense Pt I
      • ACS 2023: babyrev
      • ACS 2023: expr
      • ACS 2023: rustarm
      • ACS 2023: Maze
      • SiberSiaga 2023: Obstacles
      • SiberSiaga 2023: Packed
      • SiberSiaga 2023: Malbot
      • SiberSiaga 2023: Vacine
      • ABOH 2023: MetalPipe
      • ABOH 2023: Grape
      • iCTF 2023: RemoteC4
    • Binary Exploitation
      • HTB Cyber Apocalypse 2024: SoundOfSilence
      • LACTF 2024: pizza
      • ACS 2023: Licrackense Pt II
      • ACS 2023: Shellcoding Test
      • ACS 2023: Coding Test
      • ACS 2023: register
      • Wargames.MY 2023: Pak Mat Burger
      • SiberSiaga 2023: Password Generator
      • NahamCON CTF 2023: nahmnahmnahm
      • NahamCON CTF 2023: Weird Cookie
      • TJCTF 2023: shelly
      • TJCTF 2023: formatter
      • ångstromCTF 2023: gaga2
      • ångstromCTF 2023: leek
      • Space Heroes 2023: Rope Dancer
      • corCTF 2022: babypwn
      • corCTF 2021: Cshell
      • HTB Cyber Apocalypse 2023: Void
      • HTB Cyber Santa CTF 2021: minimelfistic
      • HTB Challenge: pwnshop
  • 🤡Clown Chronicles
    • About Me
    • Blogs
      • How to Win A CTF by Overcomplicating Things
      • Exploring Dynamic Invocation for Process Injection in C# and Rust
    • Projects
    • Memes
    • Others
Powered by GitBook
On this page
  • TL;DR
  • Challenge Overview
  • Export Program
  • Cleaning Up
  • Recompilation
  • Method 1: Overriding Definitions
  • Method 2: Replacing Functions
  • Running Angr
  • Final Script
  1. CTF Writeups
  2. Reverse Engineering

ACS 2023: expr

Expression

PreviousACS 2023: babyrevNextACS 2023: rustarm

Last updated 1 year ago

TL;DR

Multi-threaded flag checker designed to thwart Angr's symbolic execution. Subroutine functions can be exported via Headless Ghidra and flattened by converting it to sequential execution.

Challenge Overview

Expression (expr) is a simple flag checker that verifies if our user input matches the flag.

Taking a look at FUN_00104f60, the function takes in our user input as its 1st argument and spawns 101 separate threads to execute different subroutine functions.

We will now call this function asThread Pool Manager from this point onwards.

Within each subroutine function are specific conditions that we have to pass in order to release/unlock the mutex. If these conditions are not met, the program will hang indefinitely due to the mutex being locked.

Decoding all the values manually 101 times will be a pain in the ass, so it's pretty obvious that we had to rely on some kind of automated solver like Angr or z3. This challenge is a textbook scenario for applying Angr's Find Condition feature to search for a state that meets our arbitrary condition - In this case, we want the string "pass" to be printed out on stdout.

However, Angr in its current state does not support multi-threading program. Therefore, our plan is to recompile the program to force it to check our input sequentially.

Export Program

Firstly, we can select File -> Export Program from Ghidra to export the decompiled C code.

Or using Ghidra's headlessAnalyzer:

Cleaning Up

Within the decompiled code, most of the exported definitions such as eh_frame_hdr, fde_table_entry and more can be removed as they are not needed. The only components we need are:

  1. Type Definition: Define data type aliases that are Ghidra-compatible, such as typedef unsigned int undefined4 , etc.

  2. Subroutine Functions: Logical operations that verify our input, such as FUN_0010XXXX, etc.

  3. Main Boilerplate: Program Entry Point/Main Function that handles user input before passing execution control to Thread Pool Manager.

  4. Thread Pool Manager: The long list of if statements that invoke each subroutine function in a separate thread via pthread_create.

There are 2 ways to convert these subroutine functions from their multi-threaded form to sequential execution.

Recompilation

Method 1: Overriding Definitions

Redefine the signature for pthread_create. This only needs to be done once.

int pthread_create(pthread_t *thread, ulong *attr, long (*func_ptr)(long), char *arg) {
    return (*func_ptr)((long) arg);
}

Uncomment the mutex lock & unlock procedures for all 101 subroutine functions.

long FUN_001011d0(long param_1)

{
  uint uVar1;
  
  //pthread_mutex_lock((pthread_mutex_t *)&DAT_0010c070);
  uVar1 = *(uint *)(param_1 + 0x1b) >> 2 & 0x7ff;
  if (((uVar1 >> 2 ^ 0x557) + (uVar1 * 0x40 + 0x10c ^ (*(uint *)(param_1 + 0x14) & 0x7ff) >> 6) &
      0x7ff) == 0x79d) {
    //pthread_mutex_unlock((pthread_mutex_t *)&DAT_0010c070);
    return 0;
  }
  return 1;
}

We can keep the decode function intact, because pthread_create from now on will just act as a mask for all the subroutine functions.

Finally, compile the code with GNU Make:

Method 2: Replacing Functions

Once again, uncomment the mutex lock & unlock procedures for all 101 subroutine functions.

long FUN_001011d0(long param_1)

{
  uint uVar1;
  
  //pthread_mutex_lock((pthread_mutex_t *)&DAT_0010c070);
  uVar1 = *(uint *)(param_1 + 0x1b) >> 2 & 0x7ff;
  if (((uVar1 >> 2 ^ 0x557) + (uVar1 * 0x40 + 0x10c ^ (*(uint *)(param_1 + 0x14) & 0x7ff) >> 6) &
      0x7ff) == 0x79d) {
    //pthread_mutex_unlock((pthread_mutex_t *)&DAT_0010c070);
    return 0;
  }
  return 1;
}

Eliminate all occurrences of pthread_create by invoking the subroutine functions directly.

Recompile the C code with GNU Make:

Running Angr

With Angr's symbolic execution, we managed to get the flag after approximately 10 minutes.

Flag: ACS{y0u50lv3DtH33xpr35510N5!}

Final Script

#!/usr/bin/env python3

import angr
import claripy
import logging

project = angr.Project("override_definition",load_options={"auto_load_libs": False}, main_opts={"base_addr": 0})
flag_chars = [claripy.BVS(f"c_{i}", 8) for i in range(29)]  # Flag length = 29
flag = claripy.Concat(*flag_chars)

logging.getLogger('angr').setLevel('INFO')
initial_state = project.factory.entry_state(stdin=flag)

# Printable characters only (including SPACE)
for f in flag_chars:
	initial_state.solver.add(f >= 0x20)
	initial_state.solver.add(f < 0x7f)

sm = project.factory.simulation_manager(initial_state)
sm.explore(find=lambda s: b"pass" in s.posix.dumps(1))

if sm.found:
	solution_state = sm.found[0]
	solution = solution_state.solver.eval(flag, cast_to=bytes)
	print(solution)
else:
    raise Exception('Could not find the solution')

For some reason, using this scaffold from Angr CTF would always terminate the script halfway. As a workaround, I used this instead which appears to be more optimized.

🚩
template
template
100KB
override_definition.c
39KB
replace_function.c