I have this piece of ASM:
section .text
global _start
_start:
xor eax, eax
push eax ; 0 to finish the /bin//sh string
push 0x68732f2f ; //sh
push 0x6e69622f ; /bin
mov ebx, esp
mov al, 0xb
int 0x80
Which works fine if I do:
$ nasm -f elf shellcode.asm && ld -o shellcode shellcode.o
$ ./shellcode
$ # the new shell
But now, with the hex translation:
$ objdump -s shellcode
shellcode: file format elf32-i386
Contents of section .text:
8048060 31c05068 2f2f7368 682f6269 6e89e3b0 1.Ph//shh/bin...
8048070 0bcd80 ...
Used here:
const char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80";
int main(){
(*(void(*)()) shellcode)();
return 0;
}
The result is:
$ gcc -o shellcode shellcode.c
$ ./shellcode
Segmentation fault
I tried to practice my ASM and re-create the one provided here. The difference, which I can't explain, is that on his payload, he's pushing the an extra 0
and the address of the /bin//sh
string into the stack.
I though that because I was using the fastcall convention, I didn't have to setup the stack, but apparently there's more going on here.
Does anyone know why the shellcode refuses to run and why setting the stack changes "correct" it?
Edits:
When I try to run the program from gdb
I have the following result:
$ gdb ./shellcode
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
0x080484b3 in shellcode ()
And here's the readelf
command output:
$ readelf -l -S shellcode
There are 30 section headers, starting at offset 0x1150:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 080481ac 0001ac 000020 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481cc 0001cc 000040 10 A 6 1 4
[ 6] .dynstr STRTAB 0804820c 00020c 000045 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048252 000252 000008 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0804825c 00025c 000020 00 A 6 1 4
[ 9] .rel.dyn REL 0804827c 00027c 000008 08 A 5 0 4
[10] .rel.plt REL 08048284 000284 000010 08 A 5 12 4
[11] .init PROGBITS 08048294 000294 000023 00 AX 0 0 4
[12] .plt PROGBITS 080482c0 0002c0 000030 04 AX 0 0 16
[13] .text PROGBITS 080482f0 0002f0 000192 00 AX 0 0 16
[14] .fini PROGBITS 08048484 000484 000014 00 AX 0 0 4
[15] .rodata PROGBITS 08048498 000498 00001c 00 A 0 0 4
[16] .eh_frame_hdr PROGBITS 080484b4 0004b4 00002c 00 A 0 0 4
[17] .eh_frame PROGBITS 080484e0 0004e0 0000b0 00 A 0 0 4
[18] .init_array INIT_ARRAY 08049f08 000f08 000004 00 WA 0 0 4
[19] .fini_array FINI_ARRAY 08049f0c 000f0c 000004 00 WA 0 0 4
[20] .jcr PROGBITS 08049f10 000f10 000004 00 WA 0 0 4
[21] .dynamic DYNAMIC 08049f14 000f14 0000e8 08 WA 6 0 4
[22] .got PROGBITS 08049ffc 000ffc 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 0804a000 001000 000014 04 WA 0 0 4
[24] .data PROGBITS 0804a014 001014 000008 00 WA 0 0 4
[25] .bss NOBITS 0804a01c 00101c 000004 00 WA 0 0 1
[26] .comment PROGBITS 00000000 00101c 00002b 01 MS 0 0 1
[27] .shstrtab STRTAB 00000000 001047 000106 00 0 0 1
[28] .symtab SYMTAB 00000000 001600 000430 10 29 45 4
[29] .strtab STRTAB 00000000 001a30 00024e 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
Elf file type is EXEC (Executable file)
Entry point 0x80482f0
There are 9 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
INTERP 0x000154 0x08048154 0x08048154 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x00590 0x00590 R E 0x1000
LOAD 0x000f08 0x08049f08 0x08049f08 0x00114 0x00118 RW 0x1000
DYNAMIC 0x000f14 0x08049f14 0x08049f14 0x000e8 0x000e8 RW 0x4
NOTE 0x000168 0x08048168 0x08048168 0x00044 0x00044 R 0x4
GNU_EH_FRAME 0x0004b4 0x080484b4 0x080484b4 0x0002c 0x0002c R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10
GNU_RELRO 0x000f08 0x08049f08 0x08049f08 0x000f8 0x000f8 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got
Comparison with this shellcode:
Here're the differences:
Hamza (working) | me
ASM
xor eax, eax | xor eax, eax
push eax | push eax
push 0x68732f2f | push 0x68732f2f
push 0x6e69622f | push 0x6e69622f
mov ebx, esp | mov ebx, esp
push eax |
push ebx |
mov esp, ecx |
mov al, 0xb | mov al, 0xb
int 0x80 | int 0x80
Stack before the interrupt (each line is a word)
&(/bin//sh) | /bin
0 | //sh
/bin | 0
//sh |
0 |
Registers before the interrupt
eax 0xb | 0xb
ebx &(/bin//sh) | &(/bin//sh)
ecx esp | ?
As you can see, the stack changes by having the string's address and an extra 0
, and his ecx
register is set to the last esp
value. And those differences make his shellcode to work (both direcly with nasm
& ld
and inside the C
program, without any change).
Edit:
Some progress, there's one extra instruction when the payload run within the C program:
(gdb) x/14i 0x80484a0
...
0x80484b3 <shellcode+19>: mov $0xb,%al
0x80484b5 <shellcode+21>: int $0x80
0x80484b7 <shellcode+23>: add %al,(%ecx)
While I don't know (yet) why the ecx
register is incremented with the return value of the interrupt, clearing the register with xor ecx, ecx
fixes the issue.
Here's the working asm:
section .text
global _start
_start:
xor eax, eax
push eax
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
xor ecx, ecx
mov al, 0xb
int 0x80
push eax
the first time...readelf -l -S
.What doesgdb
say when you run the compiled program under it?.data
is writable and allocable but not executable. Could it be it? Also when I try toreadelf
from the asm-generated executable, I only have the.text
header that is executable.