The Student Room Group

need to overwrite RIP but the address has \x0a in it

So I've got this pwnable challenge to do. Its a pretty simple game, is a 64bit C executable.

There is a buffer overflow from a call to fgets() that overflows after 50bytes. at 94 bytes I've overwritten RIP (and can control program execution)

The problem im having is that the only address range I can redirect RIP to without the program exiting is 0x00000000400a64. Because of the way the program takes input, (this is done over a socket from a listening server), the \x0a causes a line feed, meaning \x64 is missed off and doesnt overwrite RIP properly.

Any one know how to get around this?!
Alas, your domain is mega specific. However, all fgets does is read a string from a FILE pointer. I am not too sure how you are getting an overrun as fgets specifies the number of bytes to read and if it hits the EOF marker before reading the full buffer it returns. The only thing I can suggest is you are asking to read >50 bytes into an allocated buffer of 50. If you are getting overruns, i.e. you are splatting over random areas of memory after 50 bytes, then there is no way you can recover. It like spilling coffee over your shopping list. Game over.

Good luck!
Reply 2
You'll have little luck finding an answer on here, post on StackOverflow.
Reply 3
Original post by ByEeek
Alas, your domain is mega specific. However, all fgets does is read a string from a FILE pointer. I am not too sure how you are getting an overrun as fgets specifies the number of bytes to read and if it hits the EOF marker before reading the full buffer it returns. The only thing I can suggest is you are asking to read >50 bytes into an allocated buffer of 50. If you are getting overruns, i.e. you are splatting over random areas of memory after 50 bytes, then there is no way you can recover. It like spilling coffee over your shopping list. Game over.

Good luck!


Sorry the question wasn't very well written I was up all night on a CTF.

I figured it out in the end, overwriting RIP wasn't the right way to do it. The program used a pseudo random number generator and a seed which was found in memory. The number was used to choose rock paper or scissors, so I just copied the seed into my program and sent the winning combination to the server. Really simple solution compared to trying to overwrite rip.

Cheers for the reply though!
Stack overflow would have been your best option. I'm not great with assembly, it is on my to do list though.
As far as I know, your don't overwrite RIP as its the instruction pointer.
But I have no idea how you're getting a buffer overflow when reading a string, we'd have to see the whole code to figure that one out.
Reply 5
Original post by Jared44
Stack overflow would have been your best option. I'm not great with assembly, it is on my to do list though.
As far as I know, your don't overwrite RIP as its the instruction pointer.
But I have no idea how you're getting a buffer overflow when reading a string, we'd have to see the whole code to figure that one out.


Sorry just to clarify that normally in a simple buffer over flow exploit, the aim does tend to be to overwrite rip (or eip in x86) as you're meant to overwrite it to an address that points to your shell code, either in an env variable or on the stack. The code itself had something like char buff[64] (so anything over 64 overflows eventually hitting rip).

Doesn't matter now anyway as this was the incorrect method. I was half right by overflowing the buffer. But didn't realise I could change the seed to srand() and so predict the "random" choice of rock paper and scissors. Never mind
Original post by tim_123
Sorry just to clarify that normally in a simple buffer over flow exploit, the aim does tend to be to overwrite rip (or eip in x86) as you're meant to overwrite it to an address that points to your shell code, either in an env variable or on the stack. The code itself had something like char buff[64] (so anything over 64 overflows eventually hitting rip).

Doesn't matter now anyway as this was the incorrect method. I was half right by overflowing the buffer. But didn't realise I could change the seed to srand() and so predict the "random" choice of rock paper and scissors. Never mind


Were you trying to create a buffer overflow? :s-smilie:
I'm lost in what you were trying to do exactly.
Reply 7
Original post by Jared44
Were you trying to create a buffer overflow? :s-smilie:
I'm lost in what you were trying to do exactly.


Ill explain the entire task.

so, you are given a binary written in C, its basically a rock paper scissors game. you are also given a server to connect to that, once connected, runs the game and asks for input, but more on that later.

The rock paper scissors (RPS) program runs as such.

1) ask user for their name
2) greets the user eg hi jared44
3)the program then "randomly" chooses r p or s and asks for your choice.
4) if you win, you get 1 point, and the game starts again.
5)if you lose, the game ends.

You have to beat the program 50 times in a row. when you beat the game, it then opens a file, reads the content (the flag) and then prints that to the screen.

Now to the exploit. It turned out that the buffer holding the players name was 50 bytes. Imagine char name[50]. If you enter 128 A's then you get a seg fault (cannot access memory at 0x41414141414141), so that means at 128 (or less) bytes, RIP has been overwritten.

All seems well, now all i had to do was find the exact number of bytes to fill the buffer with, minus the number of bytes for a return address so jump execution to. simple enough. There was a section in the disassembled code (seen via gdb), that was something like jle rbp-0x4 0x32 (which means, if rbp-0x4 is less than 0x32 jmp to ....) so a logical choice would be to overwrite RIP to the address just after this check, avoiding the need to beat the game 50 times.

Now, the problem. the memory address just after (and a fair few addresses before and after) this section was something like 0x0000400a81. You see that \x0a, thats a new line feed byte. So, lets say i started the program something like this at the cmd line:

$ python -c 'print "A" * 120 + "\x81\x0a\x40\x00\x00\x00"' > ./rps_game

what would happen is the game would read in 120 A's then \x81, then \x0a, but then it would create a line feed and "ignore" the rest of the input, thus not properly overwriting the memory address. This is what i was struggling with.

The real solution.

So it turned out, the game was using a seed for the random number, so it was choosing a number between 0,1,2. where 0 = r, 1 =s etc And it just so happened that the seed was located around 16 bytes after the name buffer in memory. So what you needed to do was overflow the name buffer, with say 66 A's. and then write a small script that seeded a random number generator with the same seed that you will overwrite the rps game with. This means that your answers will exactly match the vulnerable games.

all that was left to do then was connect to the server, overwrite the buffer, and then keep matching the games answers, which in the end was extremely simple
Original post by tim_123
Ill explain the entire task.

so, you are given a binary written in C, its basically a rock paper scissors game. you are also given a server to connect to that, once connected, runs the game and asks for input, but more on that later.

The rock paper scissors (RPS) program runs as such.

1) ask user for their name
2) greets the user eg hi jared44
3)the program then "randomly" chooses r p or s and asks for your choice.
4) if you win, you get 1 point, and the game starts again.
5)if you lose, the game ends.

You have to beat the program 50 times in a row. when you beat the game, it then opens a file, reads the content (the flag) and then prints that to the screen.

Now to the exploit. It turned out that the buffer holding the players name was 50 bytes. Imagine char name[50]. If you enter 128 A's then you get a seg fault (cannot access memory at 0x41414141414141), so that means at 128 (or less) bytes, RIP has been overwritten.

All seems well, now all i had to do was find the exact number of bytes to fill the buffer with, minus the number of bytes for a return address so jump execution to. simple enough. There was a section in the disassembled code (seen via gdb), that was something like jle rbp-0x4 0x32 (which means, if rbp-0x4 is less than 0x32 jmp to ....) so a logical choice would be to overwrite RIP to the address just after this check, avoiding the need to beat the game 50 times.

Now, the problem. the memory address just after (and a fair few addresses before and after) this section was something like 0x0000400a81. You see that \x0a, thats a new line feed byte. So, lets say i started the program something like this at the cmd line:

$ python -c 'print "A" * 120 + "\x81\x0a\x40\x00\x00\x00"' > ./rps_game

what would happen is the game would read in 120 A's then \x81, then \x0a, but then it would create a line feed and "ignore" the rest of the input, thus not properly overwriting the memory address. This is what i was struggling with.

The real solution.

So it turned out, the game was using a seed for the random number, so it was choosing a number between 0,1,2. where 0 = r, 1 =s etc And it just so happened that the seed was located around 16 bytes after the name buffer in memory. So what you needed to do was overflow the name buffer, with say 66 A's. and then write a small script that seeded a random number generator with the same seed that you will overwrite the rps game with. This means that your answers will exactly match the vulnerable games.

all that was left to do then was connect to the server, overwrite the buffer, and then keep matching the games answers, which in the end was extremely simple


So the random seed integer was after the name buffer, but before any memory that was not in the address space of the process. Like so:

struct game
{
char name[50];
//16 bytes worth of some data...
int random_seed;
};

So that's very easy. But your original attempt makes no sense. You were trying to set RIP when the only data you could inject was the name input? Makes no sense. Unless you know the struct was on the stack. Then there could be a return address in there that you could overwrite. But if this exe is on a server, you wouldn't know the address of the function that displays the "you win" message anyway; and there would be no way to inject your own code as that would sit on the stack (read/write but not execute permissions).
(edited 8 years ago)
Reply 9
Original post by __el_0hssa
So the random seed integer was after the name buffer, but before any memory that was not in the address space of the process. Like so:

struct game
{
char name[50];
//16 bytes worth of some data...
int random_seed;
};

So that's very easy. But your original attempt makes no sense. You were trying to set RIP when the only data you could inject was the name input? Makes no sense. Unless you know the struct was on the stack. Then there could be a return address in there that you could overwrite. But if this exe is on a server, you wouldn't know the address of the function that displays the "you win" message anyway; and there would be no way to inject your own code as that would sit on the stack (read/write but not execute permissions).


Again sorry it's a hard one for me to explain. The executable is run on a server, but you are also given the compiled code to play around with. Hence you can use gdb to view the dissasembled code.

From my analysis the name buffer was 50 bytes of memory, and then yes there was some random bytes of whatever. And then the seed, which, from gdb, you could see was all on the stack.

So, if you know the address of the "win" section of code, and you can see the overflow on the stack, it's pretty straightforward to overwrite rip and Change the execution.

My failing was part of not noticing there was a seed being held on the stack that I could overwrite.

But yeh I understand my explanation was pretty terrible!
Original post by tim_123

So, if you know the address of the "win" section of code, and you can see the overflow on the stack, it's pretty straightforward to overwrite rip and Change the execution.


By the bold part, I assume you mean where the struct ends, and rest of the stack continues? Thing is, even if you have the source-code and know exactly how it was compiled, you defo wouldn't know the address of the "win" function :smile:

Actually I just remembered something. On most OSs the stack is upside down in memory. So the injection point would be from a HIGHER memory address than the rest of the stack:

[high memory]
start of stack
injection point (injecting towards higher memory)
nearest return address
RSP
end of stack
[low memory]

Although it depends on calling convention and compiler used, as well as in which scope the struct was declared in (assuming this was written in C).
(edited 8 years ago)
Reply 11
Original post by __el_0hssa
By the bold part, I assume you mean where the struct ends, and rest of the stack continues? Thing is, even if you have the source-code and know exactly how it was compiled, you defo wouldn't know the address of the "win" function :smile:

Actually I just remembered something. On most OSs the stack is upside down in memory. So the injection point would be from a HIGHER memory address than the rest of the stack:

[high memory]
start of stack
injection point (injecting towards higher memory)
nearest return address
RSP
end of stack
[low memory]

Although it depends on calling convention and compiler used, as well as in which scope the struct was declared in (assuming this was written in C).


It was written in C. the name buffer wasn't part of a struct, it was (judging from the assembly code) something like,

int main(char* argv, int argc){
char name[50];
strcpy(name, argv[1]) //notice no check on length of input... hence this is vulnerable to a buffer overflow
int score //this memory is zeroed out so you cant overflow this to equal 0x32 (50) points.
int seed //this is where you need to overflow to
//after this theres about 60 more bytes of data before you hit RIP.

this is all rough estimates but closely ressembles the analysis in GDB.

I'm not 100% sure what's got you miffed. You can see exactly where the "win" function is, in fact you can see exactly where every instruction is in memory using gdb. Judging by the assembly code it wasn't an actual function, more like:

if(score == 50){
printf("well done %s, you win!", name);
fp = open..... //open file, print contents
}

I dont have the binary any more otherwise I'd just show you what I'm on about in gdb. All i can say is my initial attempt was spot on in terms of exploiting a buffer overflow, it just so happens you couldn't overwrite rip with a decent address because of the new line character \x0a
Original post by tim_123
In fact you can see exactly where every instruction is in memory using gdb.


But not on the remote server.
Reply 13
Original post by __el_0hssa
But not on the remote server.


actually you could do this, this person did what I did, except he persevered with finding an address to overwrite RIP with, whereas the rest of us just over wrote the seed.

https://jkrshnmenon.wordpress.com/2015/09/09/mmactf15-rps-write-up/
Original post by tim_123
actually you could do this, this person did what I did, except he persevered with finding an address to overwrite RIP with, whereas the rest of us just over wrote the seed.

https://jkrshnmenon.wordpress.com/2015/09/09/mmactf15-rps-write-up/


How did he know that the address of the instruction on a remote server was at 0x4008b4? If he knew it, then yea, it's easy; and the solution (to jump to an address which jumps to an address, is a good way of doing it).
(edited 8 years ago)
Some of these pwnage games go to great lengths to disable Address Space Layout Randomisation, memory security tagging etc. etc.

I have no problem believing that simply examining the binary was sufficient in this case, even though it never normally would be.
Original post by President Snow
Some of these pwnage games go to great lengths to disable Address Space Layout Randomisation, memory security tagging etc. etc.

I have no problem believing that simply examining the binary was sufficient in this case, even though it never normally would be.


Ah right, it was disabled! Either way overwriting the random seed is a better solution as that could work even on a normal set up.
(edited 8 years ago)
Reply 17
Original post by __el_0hssa
How did he know that the address of the instruction on a remote server was at 0x4008b4? If he knew it, then yea, it's easy; and the solution (to jump to an address which jumps to an address, is a good way of doing it).


I see what you're getting at and can't answer 100%, all I know is that it does work.
Reply 18
Original post by President Snow
Some of these pwnage games go to great lengths to disable Address Space Layout Randomisation, memory security tagging etc. etc.

I have no problem believing that simply examining the binary was sufficient in this case, even though it never normally would be.


Yeh and this was a warmup pwn. I just overthought it massively. As always!

Some of the later challenges were just rediculously hard. It's all good practice though

Quick Reply

Latest

Trending

Trending