Multiple Patches
Here is a simple example of a vulnerable C program which we will use to show how different patches can be used together:
#include <stdio.h>
int my_getline(char* buf) {
int i = 0;
while (1) {
char c = getc(stdin);
if (c == '\n') break;
buf[i++] = c;
}
buf[i] = '\0';
return i;
}
int main() {
char buf[10];
my_getline(buf);
puts(buf);
return 0;
}
And here is the disassembly of the relevant functions:
0000000000001189 <my_getline>:
1189: f3 0f 1e fa endbr64
118d: 55 push %rbp
118e: 48 89 e5 mov %rsp,%rbp
1191: 48 83 ec 20 sub $0x20,%rsp
1195: 48 89 7d e8 mov %rdi,-0x18(%rbp)
1199: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
11a0: 48 8b 05 69 2e 00 00 mov 0x2e69(%rip),%rax # 4010 <stdin@GLIBC_2.2.5>
11a7: 48 89 c7 mov %rax,%rdi
11aa: e8 e1 fe ff ff call 1090 <getc@plt>
11af: 88 45 fb mov %al,-0x5(%rbp)
11b2: 80 7d fb 0a cmpb $0xa,-0x5(%rbp)
11b6: 74 1b je 11d3 <my_getline+0x4a>
11b8: 8b 45 fc mov -0x4(%rbp),%eax
11bb: 8d 50 01 lea 0x1(%rax),%edx
11be: 89 55 fc mov %edx,-0x4(%rbp)
11c1: 48 63 d0 movslq %eax,%rdx
11c4: 48 8b 45 e8 mov -0x18(%rbp),%rax
11c8: 48 01 c2 add %rax,%rdx
11cb: 0f b6 45 fb movzbl -0x5(%rbp),%eax
11cf: 88 02 mov %al,(%rdx)
11d1: eb cd jmp 11a0 <my_getline+0x17>
11d3: 90 nop
11d4: 8b 45 fc mov -0x4(%rbp),%eax
11d7: 48 63 d0 movslq %eax,%rdx
11da: 48 8b 45 e8 mov -0x18(%rbp),%rax
11de: 48 01 d0 add %rdx,%rax
11e1: c6 00 00 movb $0x0,(%rax)
11e4: 8b 45 fc mov -0x4(%rbp),%eax
11e7: c9 leave
11e8: c3 ret
00000000000011e9 <main>:
11e9: f3 0f 1e fa endbr64
11ed: 55 push %rbp
11ee: 48 89 e5 mov %rsp,%rbp
11f1: 48 83 ec 20 sub $0x20,%rsp
11f5: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
11fc: 00 00
11fe: 48 89 45 f8 mov %rax,-0x8(%rbp)
1202: 31 c0 xor %eax,%eax
1204: 48 8d 45 ee lea -0x12(%rbp),%rax
1208: 48 89 c7 mov %rax,%rdi
120b: e8 79 ff ff ff call 1189 <my_getline>
1210: 48 8d 45 ee lea -0x12(%rbp),%rax
1214: 48 89 c7 mov %rax,%rdi
1217: e8 54 fe ff ff call 1070 <puts@plt>
121c: b8 00 00 00 00 mov $0x0,%eax
1221: 48 8b 55 f8 mov -0x8(%rbp),%rdx
1225: 64 48 2b 14 25 28 00 sub %fs:0x28,%rdx
122c: 00 00
122e: 74 05 je 1235 <main+0x4c>
1230: e8 4b fe ff ff call 1080 <__stack_chk_fail@plt>
1235: c9 leave
1236: c3 ret
The program currently reads a string from standard input and echos it back. We can run it with short strings:
$ ./getline
aaaaa
aaaaa
$ ./getline
aaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaa
*** stack smashing detected ***: terminated
[1] 194473 IOT instruction (core dumped) ./getline
We will patch this program to fix the vulnerability, by adding a second argument to the my_getline
function, and printing a message when the buffer would have overflowed. Here is the script:
from patcherex2 import *
p = Patcherex("getline")
p.patches.append(InsertInstructionPatch(0x120b,"mov esi, 0xa\n"))
p.patches.append(InsertDataPatch("my_str",b"Ran out of space\0"))
p.patches.append(InsertInstructionPatch(0x1199,"mov [rbp-0xc],esi"))
asm_string = """
cmp edx, [rbp-0xc]
jl less
SAVE_CONTEXT
lea rdi, [{my_str}]
call {puts}
RESTORE_CONTEXT
mov rsi, [rbp-0x18]
mov byte ptr [rsi+rax], 0
mov eax, edx
leave
ret
less:
"""
p.patches.append(InsertInstructionPatch(0x11c1,asm_string))
p.apply_patches()
p.save_binary()
This patch adds the buffer size as a second argument to my_getline
. To do this we insert instructions at the beginning to save the argument, and in the loop body we insert a check to see if the index is out of bounds. When it is, we print a message, which was inserted using the InsertDataPatch
. We can use the SAVE_CONTEXT
and RESTORE_CONTEXT
macros (expanded by Patcherex2) to do operations that could modify data we have in registers, like calling a function. We also insert instructions in main
to put the buffer size in esi
before calling the function. Now we can run the script to patch the binary and run the new fixed program:
$ ./getline.patched
aaaaaaaaaaaaaaaaaaaaaaa
Ran out of space
aaaaaaaaa
We have successfully fixed the bug with Patcherex2.