I was debugging a seg fault in a Linux app that was caused by a program trying to change a static constant array structure (so the data was in the read-only section of the ELF and subsequently loaded in a page that was then given read-only permission).
While in GDB I put a breakpoint on the line of assembler that did the bad store, and when it stopped there I manually performed the equivalent write action using GDB. GDB did this without any complaints, and reading the value back proved it had indeed been written. I looked in /proc/thepid/maps and that particular page was still marked as "not writeable".
So my question is: does GDB temporarily set write permissions on a read-only page, perform the write, then reset the permissions? Thanks.
does GDB temporarily set write permissions
No.
On Linux/*86, ptrace() (which is what GDB uses to read and write the inferior (being debugged) process memory) allows reads and writes to pages that are not readable/writable by the inferior, leading exactly to the confusion you've described.
This could be considered a bug in the kernel.
It should be noted that the kernel has to allow ptrace to write to normally non-writable .text section for the debugger to be able to plant breakpoints (which is done by overwriting original instruction with the breakpoint/trap instruction -- int3 via PTRACE_POKETEXT request).
The kernel doesn't have to do the same for POKE_DATA, but man ptrace says:
PTRACE_POKETEXT, PTRACE_POKEDATA
Copies the word data to location addr in the child's memory.
As above, the two requests are currently equivalent.
I believe it's that equivalentness that causes the current behavior.
When I use gdb to debug process in arm linux I can use call like call write(123,"abc",3)
How does gdb inject that call into process and recover all?
How does gdb inject that call into process and recover all?
GDB can read and write the inferior (being debugged) process memory using ptrace system call.
So it reads and saves in its own memory some chunk of instructions from inferior (say 100 bytes).
Then it overwrites this chunk with new instructions, which look something like:
r0 = 123
r1 = pointer to "abc"
r2 = 3
BLR write
BKPT
Now GDB saves the current inferior registers, sets ip to point to the chunk of instructions it just wrote, and resumes the inferior.
Inferior executes instructions until it reaches the breakpoint, at which point GDB regains control. It can now look at the return register to know what write returned and print it. GDB now restores the original instructions and original register values, and we are back as if nothing happened.
P.S. This is a general description of how "call function in inferior" works; I do not claim that this is exactly how it works.
There are also complications: if write calls back into the code that GDB overwrote, it wouldn't work. So in reality GDB uses some other mechanism to obtain suitable "scratch area" in the inferior. Also, the "abc" string requires scratch area as well.
Sometimes in GDB I want to see the control flow that got the program to where it is now. Simply put, how do I make GDB print the last x lines executed?
This is yet another use case for Reverse Debugging.
You should start process record and replay at some point:
(gdb) record
When you want to see last executed lines you can go backwards like
this:
(gdb) reverse-step 3
or
(gdb) reverse-next 3
Use this answer https://stackoverflow.com/a/1545732/72178 to actually
print the next N executed lines.
You simply cannot do that (easily) in gdb, because the execution trace of any program is not kept (and keeping it would be very expensive : it would slow down a lot the execution, and it would use a lot of resources - memory & disk space).
You can however use the backtrace or bt command of gdb to show the call stack, that is the current instruction pointer in the current function, the calling function, the calling function of the calling function, and so forth
BTW, if you really wanted it, you might script recent gdb using Python or Guile to give you such information. You certainly could keep the entire trace (by scripting around the step & backtrace functionalities).
If you dont want the long sschpeal head the the last paragraph-->
I found a buffer overflow vulnerability in a program that is using gets() to fill a function's local 1024-char* buffer. It's on Sparc Solaris 5.8 (sun4u) 32-bit.
The first obstacle to overcome was the tch was not letting me manually input > 257 chars
(256 if I want to be able to hit enter ;)
To bypass this, I have been executing /bin/sh and stty raw and I can effectively overflow the buffer now with > 1095 chars.
(Note : I have to use Ctrl-J to do line-feeds/enter , though I haven't researched stty raw to examine why this change occurs.
My issue is this: it is now time to not only overflow the buffer but also write new return address / preserve %fp in hex codes. But since I know of no way to manually enter hex codes from inside a terminal program, I figured I could find a way to use C and have it execute/interact with the vulnerable program and eventually send it my custom buffer.
HOWEVER, if I had a way to manually enter / copy paste hex bytes, I could just do something EASY like this!!!
perl -e 'print "n" . "A"x1094 . "\xff\xbe\xf5\x58" . "\xff\xbe\xff\x68" . "\0"'
(if you're wondering why I am printing 'n' it is because the vulnerable program checks for a yes/no # index 0 of the string)
because I know no way to manually paste such hex-information, I have been trying in C.
In C, I craft the special buffer and have been learning to popen() the vulnerable program ("w") and fputs my buffer, but it has been working iffy at best. (popen and IPC is all new to me)
(I also tried piping/dup2ing and i got NO results, no evidence of effective string output/input) not sure what is going wrong, and I experimented much with the code and later abandoned it.
The best to depict the output from my 'popen' program is that there is a segfault in the vulnerable program only by delimiting the buffer at indexes [1096->1099], this is effectively the location of the function's %fp, so it seemed normal # first. However, delimiting the string at indexes HIGHER than this leaves the programing working fine (WTF)!!! And that sort of behavior makes me think WTF!!? That is not the same behavior as manually pasting, as going more chars most definitely changes seg fault -> bus error, because I will be next overwriting the return address followed by whatever possibly important info in that stack frame and beyond!!
Is the whole string not actually getting sent in one bang?!?!? I heard something about buffer fflush() issues from the popen() manpage, but I dont understand that talk!!
It's my first time using popen(), and there is more behavior that I have deemed strange-> if i stop fputs()ing data , the vulnerable program goes into an infinite loop, repeatedly printing the last output string that it NORMALLY would
only print once,
but in this case, whenever i stop fputs'ing, the thing starts infinitely printing out. Now, I expected that if I am not outputting, wouldn't the program just sit and wait for more input like a good duck. ??? apparently not. apparently it has to keep on pissing and moaning that I need to enter the next string!! is this normal behavior with popen?! Perhaps it is due to my popen' program exiting and closing with pclose(), before actually finishing (but i was expecting a buffer overflow and i dont know why I am not getting it like I could when pasting manually)
Note: I am using "\r\n" to signal the vulnerable program to do a 'return' , I am not sure the equivalent of CTRL-J / Enter key (which enter key does not work in raw tty). I am also not sure if raw tty is even necessary when piping a buffer.
then I thought I try to be clever and cat the strings to a file and then do a pipe via command line. I have no idea if u can pipe like this to a program expecting inputs
in this form, I could not even get a single overflow!! i.e.
printf "\r\n" > derp && perl -e 'print "n" . "A"x1025' >> derp && printf "\r\n" >> derp
cat derp | ./vuln
Now, rewind <-> back in tsh, i said I have a 257 char limit, and i needed to do ONE LESS THAN THAT if i wanted to be able to hit enter and have the program continue operation. So, perhaps \r\n is not right here, cause that's 2 chars. either that or you just Cannot cat into a program like this. But I AM using \r\n in my C programs to tell the vulnerable program that I have hit enter, and they are at least mildly more functional (not really), though still not overflowing the buffer in the same fashion as manually pasting my trash buffer.
ARGh!!!
Also, using just one or the other: '\r' or '\n' was most definitely not working! is there another control char out there I am missing out on? And is it possible that this could be one of my issues with my programs???
but basically my whole problem is I cant' seem to understand how to create a program to run and interface with a command-line executable and say hey!!! Take this whole buffer into your gets(), i know you'd really love it!! just as I would if I was running the program from terminal myself.
And i know of no way to manually paste / write hex codes into the terminal, is the whole reason why i am trying to write an interacting program to
craft a string with hext bytes in C and send to that program's gets()!!!!
If you jumped to this paragraph, i want you also to know that I am using specifically /bin/bash and stty raw so that I could manually input more than 257 chars (not sure if I NEED to continue doing this if I can successfully create an interacting program to send the vulnerable program the buffer. maybe sending a buffer in that way bypasses tch' terminal 257 char limit)
Can anyone help me!?!?!?!?!
The popen call is probably the call you want. Make sure to call pclose after the test is finished so that the child process is properly reaped.
Edit Linux man page mentioned adding "b" to the mode was possible for binary mode, but POSIX says anything other than "r" or "w" is undefined. Thanks to Dan Moulding for pointing this out.
FILE *f = popen("./vuln", "w");
fwrite(buf, size, count, f);
pclose(f);
If the shell is reading with gets(), it is reading its standard input.
In your exploit code, therefore, you need to generate an appropriate overlong string. Unless you're playing at being expect, you simply write the overlong buffer to a pipe connected from your exploit program to the victim's standard input. You just need to be sure that your overlong string doesn't contain any newlines (CR or LF). If you pipe, you avoid the vagaries of terminal settings and control-J for control-M etc; the pipe is a transparent 8-bit transport mechanism.
So, your program should:
Create a pipe (pipe()).
Fork.
Child:
connect the read end of the pipe to standard input (dup2()).
close the read and write ends of the pipe.
exec the victim program.
report an error and exit if it fails to exec the victim.
Parent:
close the read end of the pipe.
generates the string to overflow the victim's input buffer.
write the string to the victim down the pipe.
Sit back and watch the fireworks!
You might be able to simplify this with popen() and the "w" option (since the parent process will want to write to the child).
You might need to consider what to do about signal handling. There again, it is simpler not to do so, though if you write to a pipe when the receiver (victim) has exited, you will get a SIGPIPE signal which will terminate the parent.
Nothing is yielding results.
Let me make highlights of what I suspect are issues.
the string that I pipe includes a \n at the beginning to acknowledge the "press enter to continue" of the vulnerable program.
The buffer I proceed to overflow is declared char c[1024]; now I fill this up with over 1100 bytes. I don't get it; sometimes it works, sometimes it doesn't. Wavering factor is if I am in gdb (being in gdb yields better results). but sometimes it doesn't overflow there either. DUE TO THIS, I really believe this to be some sort of issue with the shell / terminal settings on how my buffer is getting transferred. But I have no idea how to fix this :(
I really appreciate the help everybody. But I am not receiving consistent results. I have done a number of things and have learned a lot of rough material, but I think it might be time to abandon this effort. Or, at least wait longer until someone comes through with answers.
p.s.
installed Expect, :) but I could not receive an overflow from within it...
I seemed to necessitate Expect anyways, because after the pipe is done doing its work I need to regain control of the streams. Expect made this very simple, aside from that fact that I can't get the program to overflow.
I swear this has to do something with the terminal shell settings but I don't have a clue.
Another update
It's teh strangest.
I have actually effectively overwritten the return address with the address of a shellcode environment variable.
That was last night, Oddly enough, the program crashed after going to the end of the environment variable, and never gave me a shell. The shellcode is handwritten, and works (in an empty program that alters main's return address to the addr of the shellcode and returns, simply for test purposes to ensure working shellcode). In this test program Main returns into my SPARC shellcode and produces a shell.
...so.... idk why it didn't work in the new context. but thats the least of my problems. because the overflow it's strange.....
I couldn't seem to reproduce the overflow after some time, as I had stated in my prior post. So, i figured hey why not, let's send a bigger,more dangerous 4000 char buffer filled with trash "A"s like #JonathanLeffler recommended, to ensure segfaulting. And ok let's just say STRANGE results.
If I send less than 3960 chars, there will NOT be an overflow (WTF?!?!), although earlier i could get overflow at times when doing only about 1100 chars, which is significantly less, and that smaller buffer would overwrite the exact spot of return address (when it worked .*cough)
NOW THE strangest part!!!
this 'picky' buffer seems to segfault only for specific lengths. But i tried using gdb after sending the big 4000 char buffer, and noticed something strange. Ok yes it segfaulted, but there were 'preserved areas,' including the return address i previously was able to overflow, is somehow unharmed, and u can see from the image (DONT CLICK IT YET) Read the next paragraph to understand everything so u can properly view it. I am sure it looks a mess without proper understanding. parts of my crafted buffer are not affecting certain areas of memory that I have affected in the past with a smaller buffer! How or why this is happening. I do not know yet. I have no idea how regular this behavior is. but i will try to find out .
That image takes place about 1000 bytes in from the buffer's start address. you can see the 'preserved memory segments', embedded between many 0x41's from my buffer ("A" in hex) . In actuality, address 0xffbef4bc holds the return address of 0x0001136c, which needs to be overwritten, it is the return address of the function that called this one, 'this one' = the function that holds the vulnerable buffer. we cannot write (*the function that vulnerable buffer belongs to)*'s return address due to the nature of stack windows in SPARC -- that return address is actually BELOW the address of the buffer, unreachable, so therefore we must overwrite the return address of the function above us. aka our caller ;)
Anyways the point is that I was also able to previously overflow that return address sometimes with a smaller buffer. So WTF is up with these gaps!!?!??! Shouldnt a larger buffer be able to overflow these, esp. if the smaller buffer could (though not consistently).. Whatever, here's the image.
[image] http://s16.postimage.org/4l5u9g3c3/Screen_shot_2012_06_26_at_11_29_38_PM.png
Is it possible to control gdb from the debugged program? I'm hoping for a library that can help with this, ideally with an API such as gdb_sendcmd("bt"), but I can live with something like connecting to local gdb via a socket.
Primary use case is programmatically adding a data breakpoint to monitor when a certain memory address next gets modified. Target language is, naturally, C; anything applicable to it can be reused with C++ and Objective-C.
Answer from Employed Russian has solved my direct problem, but I'd still love to know how I can run GDB commands programmatically from the debugged program. It may speed up some debugging if I could simply add code to the project instead of writing extra startup commands for GDB that would create breakpoints with attached commands.
So if there is a way to run commands programmatically, I'd still love to hear it ;)
The Python interface exported by GDB allows you to do many things. Maybe something like this would fit your requirements:
import gdb
CMD_FCT = "gdb_run"
CMD_NAME = "str"
class GdbRunBreakpoint(gdb.Breakpoint):
def __init__(self):
gdb.Breakpoint.__init__(self, CMD_FCT, internal=1)
self.silent = True
def stop(self):
cmd = gdb.parse_and_eval(CMD_NAME).string()
gdb.execute(cmd)
return False
GdbRunBreakpoint()
(just write than in a file, and source it from your .gdbinit file)
and on the application side:
void gdb_run(char *str) {}
int main () {
gdb_run("where");
}
I think the code is straight forward, but as I mentioned in https://stackoverflow.com/a/8884512/341106, not everything is allowed in the stop callback, GDB is in an intermediate state, but many things will work as expected.
EDIT: needless to say, this won't work if you app is not compiled with debug symbols!
Is it possible to control gdb from the debugged program?
No. If the program could do that, and (say) disabled all breakpoints, how would you debug it?
Primary use case is programmatically adding a data breakpoint to monitor when a certain memory address next gets modified.
This often comes up in a context like this: on Nth invocation of foo(), a (local or global) variable bar gets unexpectedly clobbered, and you want to find the culprit.
If that is indeed your problem, then set a breakpoint on the line where you know the value of bar is still good (just after it has been initialized). Set ignore count (using ignore command) on that breakpoint to N-1, and then, when the breakpoint is hit, set the watchpoint on bar.
You can even attach commands to the breakpoint:
commands 1 # assuming this was the first breakpoint
watch bar
continue
end
so the watchpoint is attached automatically.