• 原创 Reverse | Carpet 地毯覆盖问题


    第十三届全国大学生信息安全竞赛-创新实践能力赛 Build 环节

    完整包下载

    赛题设计说明

    题目信息:

    • 题目名称: carpet(地毯)

    • 预估难度: 中等偏易

    题目描述:

    本题属于 Reverse 类型,核心考点是算法设计能力。

    程序在给 64x64 出平面中不能覆盖的方格之后,做题者需要构造

    1365 块 L 形地毯的放置形态和位置,以保证除了给定方格其余方格

    都被完美覆盖,可以通过数学证明双向解法唯一。

    如果把给定方格视为完整消息串,构造出的放置方法为摘要信息,

    那么 exp 可以视为一个注册机,根据给出的不同坐标进行 Hash。

    题目考点(至少2点)

    • Linux 平台 ELF 文件的逆向分析

    • 信息搜集与二进制工具的使用

    • 分治算法设计与实现能力

    思路简述

    • 在相应 Linux 环境下运行本地或者远程程序,得到预定的 hint

    • 将二进制 ELF 文件拉近相应工具进行反汇编,查壳、保护机制等

    • 设计利用的算法(分治),并使用编程实现

    • 在 pwntools 等模块下完成 OI 交互,成功 getshell

    题目提示(至少3点)

    • 运行程序后提示平面地图的大小
    • 运行程序后提示地毯形状对应的编号
    • 利用成功/失败后程序会进行相应反馈
    • 内嵌后门函数 getshell()

    其他补充

    • 编译环境:
      System: Ubuntu 16.04.6 LTS
      gcc: 5.4.0 20160609
      glibc: ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23
    • 测试环境:
      System: Ubuntu 16.04.6 LTS
      gcc: 5.4.0 20160609
      glibc: ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23

    Writeup

    拿到 ELF 文件和远程地址,首先查看一下保护

    在相应环境运行一下,得到一个很重要的 hint

    初步理解就是需要在 64x64 的正方形中用 L 形地毯覆盖完全,而单独给定的一个坐标不能被覆盖

    1)64x64 = 4096,4096 / 3 = 1365 ······ 1

    2)可以用数学归纳法证明边长为 2^n 的正方形可以被该方式完美覆盖

    简单推导之后发现本题的解是一定存在的

    为了印证游戏规则,还需要把该文件拉入反汇编软件进行更加详细的分析

    map 看起来就是保存地图的函数

    [v10,v11] 是用随机数生成的坐标

    随后读入了 1365 组数,每组由三个整数 v5,v6,v7 组成,传入 cover() 当参数

    定位到 cover() 函数查看:

    发现有很多左移 6 位的操作,2^6 = 64,和此前提示的 64x64 形状相同

    判断出 map 是一个二维数组,a1 和 a2 是 map 的两个下标,a3 是此前绘出的地毯形状

    那么这个函数的功能就是在 [a1,a2] 为中心的位置 “铺上” 相应形状的地毯

    回到 main() 函数:

    在全部读入结束之后调用了 check(v10,v11),其中 [v10,v11] 是之前给的不能被覆盖的坐标

    看函数名就知道这大概是一个验证覆盖是否完美的程序

    check() 函数:

    果然是在 [0,64) 的横纵范围遍历 map,遍历到 [a1,a2] 的情况单独处理

    验证是否除了 [a1,a2] 以外的所有方格都被完美覆盖一次

    如果验证成功,则返回 True,随后 main() 函数会调用 get_shell()

    分析到这里我们的任务目标就很清晰了,就是要构造合法的输入铺设地毯,

    这里利用了 pwntools 模块进行 OI 操作

    开始设计算法:

    这个问题,似乎无从下手,于是我们可以先考虑最简单的情况,既正方形是 2x2 时

    这时,无论不能覆盖四个格子中的哪个,我们都可以用一块毯子填满

    继续考虑 4x4 的情况

    我们已经知道了解决 2x2 的格子中有一个障碍的情况如何解决,因此我们可以尝试构造这种情况

    首先,显然可以将 4x4 的盘面划分成 4 个2*2的小盘面,其中一块已经存在一个障碍了

    而我们只需在正中间的 2*2 方格中放入一块地毯,就可以使所有小盘面都有一个障碍

    于是,4x4 的情况就解决了

    我们可以推广到一般情况,即当 (2k)x(2k) 时(在这道题中 k = 6 ),

    我们均可以将问题划分为 4 个 (2(k-1))x(2(k-1)) 的子问题,然后分治算法递归解决

    于是编写 exp:

    from pwn import *
    import re
    cnt=0
    def prt(px, py, t):
        global cnt
        io.send(str(px)+' '+str(py)+' '+str(t)+'
    ')
        io.recv(timeout=0.01)
        cnt = cnt + 1
        print cnt
    def fun(siz, x, y, tx, ty):
        if (siz==1):
            if (tx==x and ty==y+1):
                prt(x+1, y, 1)
            elif (tx==x+1 and ty==y):
                prt(x, y+1, 2)
            elif (tx==x and ty==y):
                prt(x+1, y+1, 3)
            elif (tx==x+1 and ty==y+1):
                prt(x, y, 4)        
            else:
                prt(x, y, 5)
        else:
            tmp = ((1<<siz)+1)>>1
            if (tx<x+tmp and ty<y+tmp):
                fun(siz-1, x, y, tx, ty)
                fun(siz-1, x, y+tmp, x+tmp-1, y+tmp)
                fun(siz-1, x+tmp, y, x+tmp, y+tmp-1)
                fun(siz-1, x+tmp, y+tmp, x+tmp, y+tmp)
                prt(x+tmp, y+tmp, 3)
            elif (tx<x+tmp and ty>=y+tmp):
                fun(siz-1, x, y, x+tmp-1, y+tmp-1)
                fun(siz-1, x, y+tmp, tx, ty)
                fun(siz-1, x+tmp, y, x+tmp, y+tmp-1)
                fun(siz-1, x+tmp, y+tmp, x+tmp, y+tmp)
                prt(x+tmp, y+tmp-1, 1)
            elif (tx>=x+tmp and ty<y+tmp):
                fun(siz-1, x, y, x+tmp-1, y+tmp-1)
                fun(siz-1, x, y+tmp, x+tmp-1, y+tmp)
                fun(siz-1, x+tmp, y, tx, ty)
                fun(siz-1, x+tmp, y+tmp, x+tmp, y+tmp)
                prt(x+tmp-1, y+tmp, 2)
            else:
                fun(siz-1, x, y, x+tmp-1, y+tmp-1)
                fun(siz-1, x, y+tmp, x+tmp-1, y+tmp)
                fun(siz-1, x+tmp, y, x+tmp, y+tmp-1)
                fun(siz-1, x+tmp, y+tmp, tx, ty)
                prt(x+tmp-1, y+tmp-1 ,4)
    context.log_level = 'debug'
    io = process('./target')
    io.recvuntil("Don't cover [")
    message = io.recvuntil(']
    ')
    num = re.findall(r'd+',message)
    fun(6, 0, 0, int(num[0]), int(num[1]))
    io.recv(timeout=0.01)
    io.interactive()
    

    成功 getshell(),本题完成

  • 相关阅读:
    php升级5.3到5.4,5.5,5.6
    JNI NDK开发Crash错误定位 调试
    【转】移动端App测试实用指南
    【转】web测试内容及工具经典总结
    什么是比特币(bitcoin)
    读《活着》
    读《我们终将逝去的青春》
    自动make工具--CMake
    如何像黑客一样思考_转
    httpd在嵌入式中应用
  • 原文地址:https://www.cnblogs.com/zhwer/p/13563039.html
Copyright © 2020-2023  润新知