In the Pwn trilogy, we exploited the format print function to achieve remote code execution (RCE), during which we navigated through relocation and the Global Offset Table (GOT). While the Pwn trilogy is excellent, it is limited to the GNU/Linux platform, whereas most desktop users prefer Microsoft Windows. Unlike GNU/Linux, Microsoft Windows ships system libraries with the operating system and offers a comprehensive runtime environment.
Today, let’s focus on the Process Environment Block (PEB) and the Loader Record (LDR), which provide essential information about the base addresses of relocated program images and libraries. On Windows, the PEB address can be found at the thirteenth entry of the Thread Information Block (TIB). The fs register tracks the TIB in a 32-bit Windows environment. Meanwhile, in a 64-bit environment, the gs register tracks the TIB. Microsoft Windows supports 32-bit and 64-bit executables on most x86_64 platforms, setting the appropriate environment based on the machine field in the executable file’s header.
In this final part of our Pwn trilogy, we will tackle an actual CTF challenge with limitations. Previously, we explored the format print function in the C standard library, discussed the format print attack, and covered Address Space Layout Randomization (ASLR). We also learned how to bypass ASLR. To wrap things up, we will demonstrate how to manipulate data at arbitrary memory addresses on the stack using the techniques from the previous parts.
The challenge is from SaplingCTF 2023, in which we participated as team Senioritis. We were the only ones to solve the last Pwn challenge, the final assignment of CPSC233. This post also serves as a write-up for the challenge. We highly recommend that readers review the previous parts of the Pwn trilogy before diving in. For those with a strong foundation and thorough mastery of the earlier parts of the trilogy, you will enjoy it.
Reconnaissance
The challenge includes an ELF binary and a C source code file. The C source code file exposes the programming logic of the binary executable. However, understanding lower system details like stack frame layout requires additional work. For instance, similar to the first and second parts, we could either statically disassemble the binary or set a runtime breakpoint under a debugger to analyze the stack frames. This is how we worked out the stack frame of the main function.
In the second part of our Pwn trilogy, we will explore the format print function further. Part one covered the format print function from the C standard library, basic concepts of the format string attack and some information leak techniques. Here, we will delve deeper into the format print function and demonstrate how to manipulate data using a user-controllable format string.
Indirect memory writing
Like indirect memory reading discussed in part one, indirect memory writing allows arbitrary access to a foreign entity. Instead of dumping the memory chunk at an arbitrary address, indirect memory writing overrides data at the designated address in a controlled manner. The ability to modify arbitrary data in the target process’s virtual memory is crucial, as it is a critical step in taking over the target process’s control flow.
Indirect memory writing shares some analogies to indirect memory reading. In indirect memory writing, the target address is loaded onto the process’s stack, treated as a pointer, and replaced the dereferenced virtual memory location with an integer value. Unlike indirect memory reading, which retrieves an arbitrary-sized segment from the target address, indirect memory writing only overwrites the target address with a fixed-sized integer determined by the length modifier of the conversion specifier in the format string. The following example demonstrates how to perform indirect memory writing.
Welcome to the first part of the Pwn trilogy. Our journey begins with exploring the glibc implementation of the format print function printf from the C standard library. This will pave the way for direct and indirect memory reading, which is instrumental for information leaking. In the upcoming parts of this trilogy, we will delve deeper into the format print function, demonstrate how to manipulate data with format printing and explain how it facilitates remote code execution (RCE). Additionally, we will discuss a system-level countermeasure to binary exploitation (Pwn) and how to bypass it. Finally, we will demonstrate the application of these skills in a real CTF challenge.
Format print basic
In C programming, the format print function is a crucial tool for output operations, enabling developers to present information to users in a structured and readable format. Its versatility allows for printing various data types, making it an indispensable asset for debugging, user interaction, and data presentation in software development. However, this power retains pricy vulnerabilities, particularly in format string attacks. These vulnerabilities, if not understood and addressed, can be exploited to compromise the security of the software.
To understand how the format string attack works, we can start by looking at the position-parameterized placeholder arguments, which are in the form of %m$p, where m represents the position parameter of the placeholder and p represents the actual conversion specifier. To further explore the relationship between placeholders and the arguments of the format print function, let’s consider an example: printf("%d %c %x", 1, 121, 65);.
From the challenge description, we could read two major characters Olivia and m4pl3. There is a hint in the challenge description; reading one possible password of m4pl3 could be syrup.
Last login: Sun Feb 5 08:13:41 2023 from 128.189.162.194
__| __|_ ) _| ( / Amazon Linux 2 AMI ___|\___|___|
https://aws.amazon.com/amazon-linux-2/ 14 package(s) needed for security, out of 14 available Run "sudo yum update" to apply all updates. [saplingctf@ip-10-0-15-77 ~]$ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc pfifo_fast state UP group default qlen 1000 link/ether 02:84:9a:aa:58:de brd ff:ff:ff:ff:ff:ff inet 10.0.15.77/20 brd 10.0.15.255 scope global dynamic eth0 valid_lft 3154sec preferred_lft 3154sec inet6 fe80::84:9aff:feaa:58de/64 scope link valid_lft forever preferred_lft forever [saplingctf@ip-10-0-15-77 ~]$ cat .ssh/known_hosts [10.0.138.178]:12345 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKl+ZqnOze97Xg+aMswh8piTLgK4fZyY7FOxntCZqR/38ujqWt66xteRtCEyMyNBzFplGg3Ajuv9oo1o9JV0k+Q= [saplingctf@ip-10-0-15-77 ~]$
By logging into the bastion host, we awarded that the bastion host locates in a local area network(LAN) classified as a class A private network per RFC 1918. Given the address space of such a vast network segment, we checked clues of remote hosts on the bastion host instead of scanning the whole network. We found a host [10.0.138.178]:12345 from the know_hosts file.
Starting Nmap 6.40 ( http://nmap.org ) at 2023-02-05 08:22 UTC Nmap scan report for ip-10-0-138-178.ca-central-1.compute.internal (10.0.138.178) Host is up (0.00035s latency). Not shown: 65533 closed ports PORT STATE SERVICE 111/tcp open rpcbind 12345/tcp open netbus
Nmap done: 1 IP address (1 host up) scanned in 10.16 seconds [saplingctf@ip-10-0-15-77 ~]$ nmap -Pn -sC -sV -p111,12345 10.0.138.178
Starting Nmap 6.40 ( http://nmap.org ) at 2023-02-05 08:24 UTC Nmap scan report for ip-10-0-138-178.ca-central-1.compute.internal (10.0.138.178) Host is up (0.00049s latency). PORT STATE SERVICE VERSION 111/tcp open rpcbind 2-4 (RPC #100000) | rpcinfo: | program version port/proto service | 100000 2,3,4 111/tcp rpcbind |_ 100000 2,3,4 111/udp rpcbind 12345/tcp open ssh OpenSSH 7.4 (protocol 2.0) | ssh-hostkey: 2048 17:b1:9e:37:25:64:01:68:f5:d5:f9:2c:34:74:98:8a (RSA) |_256 f4:31:75:06:a6:b0:c6:36:0e:a4:78:f2:a5:bf:4c:92 (ECDSA)
Service detection performed. Please report any incorrect results at http://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 11.27 seconds [saplingctf@ip-10-0-15-77 ~]$