PWN-College-Writeup


pwn.college

https://pwn.college/program-security/reverse-engineering

20240610223520

遗留思考的问题

  1. Linux的权限管理是如何管理的,用户权限、读写权限、运行程序的权限、以及程序是否拥有读取文件的权限?

  2. 调试器和被调试程序之间的关系?如何完成调试过程,他们之前的权限关系是怎么样的?

尝试

由于程序在最初会读取flag文件的内容去生成种子,所以我想能不能通过调试这个程序来获取其在内存中存放的flag文件的内容。但还是权限的问题,因为本身我hacker用户是没有读取flag文件的权限的,而baby这个程序拥有读取flag文件的权限,我拥有运行baby程序的权限。但是调试程序虽然可以调试baby程序,可是调试程序没有读取flag文件的权限,所以调试程序哪怕可以调试baby程序,依旧会在程序断言处报错。

fd = open("/flag", 0);
if ( fd < 0 )
  __assert_fail("fd >= 0", "<stdin>", 0x11u, "flag_seed");
if ( read(fd, buf, 0x80uLL) <= 0 )
  __assert_fail("read(fd, flag, 128) > 0", "<stdin>", 0x12u, "flag_seed");

解题思路

这个跟以往不同的是,各个寄存器、系统调用、指令类别对应的特征数之前是确定的。比如a对应0x01imm对应0x01。现在它会根据flag文件中的内容生成一个数,然后将这个数作为种子,随机确定各个寄存器、系统调用、指令类别所对应的特征数。

观察下面这个函数,就可以发现这个seed在flag文件中的内容确定之后,就变成确定的了。而根据之前对随机函数的了解,如果种子是确定的话,那么多次运行生成的随机数序列是相同的,譬如无论运行多少次,生成的都是1、2、3、4

所以,后续我们要做的,就是根据特征数0x1、0x2、0x4、0x8、0x10、0x20、0x40、0x80尝试其对应的寄存器、系统调用、指令类别。

unsigned __int64 flag_seed()
{
  unsigned int seed; // [rsp+4h] [rbp-9Ch]
  unsigned int i; // [rsp+8h] [rbp-98h]
  int fd; // [rsp+Ch] [rbp-94h]
  __int64 buf[17]; // [rsp+10h] [rbp-90h] BYREF
  unsigned __int64 v5; // [rsp+98h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  memset(buf, 0, 128);
  fd = open("/flag", 0);
  if ( fd < 0 )
    __assert_fail("fd >= 0", "<stdin>", 0x11u, "flag_seed");
  if ( read(fd, buf, 0x80uLL) <= 0 )
    __assert_fail("read(fd, flag, 128) > 0", "<stdin>", 0x12u, "flag_seed");
  seed = 0;
  for ( i = 0; i <= 0x1F; ++i )
    seed ^= *((_DWORD *)buf + (int)i);
  srand(seed);
  memset(buf, 0, 0x80uLL);
  return __readfsqword(0x28u) ^ v5;
}

没想到这次居然一次就过了,看来还是有在慢慢进步的!

def write_binary_file(file_path, data_list):
    # 以二进制写模式打开文件
    with open(file_path, 'wb') as f:
        for data in data_list:
            # 将二进制数据写入文件
            f.write(data)

# 示例数据:将几个二进制块写入文件
data_list = [
    # b'\x20\x20\x20' # STM
    b'\x40\x10\x10' # IMM a = \x10
    b'\x40\x2f\x08' # IMM b = \x2f
    b'\x01\x08\x10' # stm *a = b

    b'\x40\x11\x10' # IMM a = \x11
    b'\x40\x66\x08' # IMM b = \x66
    b'\x01\x08\x10' # stm *a = b

    b'\x40\x12\x10' # IMM a = \x12
    b'\x40\x6c\x08' # IMM b = \x6c
    b'\x01\x08\x10' # stm *a = b
    
    b'\x40\x13\x10' # IMM a = \x13
    b'\x40\x61\x08' # IMM b = \x61
    b'\x01\x08\x10' # stm *a = b

    b'\x40\x14\x10' # IMM a = \x14
    b'\x40\x67\x08' # IMM b = \x67
    b'\x01\x08\x10' # stm *a = b
    
    b'\x40\x15\x10' # IMM a = \x15
    b'\x40\x00\x08' # IMM b = \x00
    b'\x01\x08\x10' # stm *a = b

    b'\x40\x00\x08' # IMM b = \x00
    b'\x40\x10\x10' # IMM a = \x10
    b'\x20\x10\x80' # open  -- return file -> a

    b'\x40\x60\x40', # imm c = 0x60  
    b'\x40\x20\x08', # imm b = 0x20  
    b'\x20\x10\x10', # read(a, b, c) --return bytes -> a


    b'\x40\x60\x40', # imm c = 0x60   
    b'\x40\x01\x10', # imm a = 0x01 
    b'\x20\x10\x02', # write  ---return bytes -> a

    b'\x20\x10\x20' # exit
]

"""
STM \x01       i    sleep
JMP \x02       f    write    
STK \x04       d     
CMP \x08       b    read_code
LDM \x10       a    read_memory
SYS \x20            exit
IMM \x40       c
ADD \x80       s    open

"""
# 目标文件路径
file_path = r'/home/hacker/Test/output3'

# 调用函数写入二进制数据
write_binary_file(file_path, data_list)

# 验证写入的数据
with open(file_path, 'rb') as f:
    content = f.read()
    print(content)

文章作者: 美食家李老叭
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 美食家李老叭 !
评论
  目录