[原创]利用auxv控制canary
利用绕过canary的另一种方法。通过修改AUXV(Auxiliary Vector)结构体使 canary 值可控。因为在ld的时候,canary是通过AUXV中的一个成员生成的。
2017-TCTF-Final-upxof
题目文件
(文件在附件中也上传了)
分析
happy@ubuntu ~/pwn/20170602-TCTF-Final/pwn-upxof checksec upxof
[*] '/home/happy/pwn/20170602-TCTF-Final/pwn-upxof/upxof'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
Packer: Packed with UPX
发现upx有壳,upx -d一下(附件中depack文件时脱壳后的),然后发现脱壳后的程序有canary
happy@ubuntu ~/pwn/20170602-TCTF-Final/pwn-upxof checksec depack
[*] '/home/happy/pwn/20170602-TCTF-Final/pwn-upxof/depack'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
脱壳后的main函数,很明显gets有个栈溢出。但是有canary,这里通过修改AUXV中的AT_RANDOM来控制我们的canary,后面会介绍。
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [rsp+0h] [rbp-410h]
unsigned __int64 v5; // [rsp+408h] [rbp-8h]
v5 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
printf("let's go:", 0LL);
gets(&v4);
return 0;
}
Auxiliary Vector & Canary原理分析
简言之,canary是由ld.so进行初始化的,而这个值又是通过Auxiliary Vector
参考
https://www.elttam.com.au/blog/playing-with-canaries/auxv结构可以在elf/elf.h里看到:
/* Auxiliary vector. */
/* This vector is normally only used by the program interpreter. The
usual definition in an ABI supplement uses the name auxv_t. The
vector is not usually defined in a standard <elf.h> file, but it
can't hurt. We rename it to avoid conflicts. The sizes of these
types are an arrangement between the exec server and the program
interpreter, so we don't fully specify them here. */
typedef struct
{
uint32_t a_type; /* Entry type */
union
{
uint32_t a_val; /* Integer value */
/* We use to have pointer elements added here. We cannot do that,
though, since it does not work when using 32-bit definitions
on 64-bit platforms and vice versa. */
} a_un;
} Elf32_auxv_t;
typedef struct
{
uint64_t a_type; /* Entry type */
union
{
uint64_t a_val; /* Integer value */
/* We use to have pointer elements added here. We cannot do that,
though, since it does not work when using 32-bit definitions
on 64-bit platforms and vice versa. */
} a_un;
} Elf64_auxv_t;
这是一个entry struct,以AT_HWCAP为例,这个结构体就会是p64(entry_type_no) + a_un
这个a_type的取值可以参见源码,以下列举部分
/* Legal values for a_type (entry type). */
#define AT_NULL 0 /* End of vector */
#define AT_IGNORE 1 /* Entry should be ignored */
#define AT_EXECFD 2 /* File descriptor of program */
#define AT_PHDR 3 /* Program headers for program */
#define AT_PHENT 4 /* Size of program header entry */
#define AT_PHNUM 5 /* Number of program headers */
#define AT_PAGESZ 6 /* System page size */
#define AT_BASE 7 /* Base address of interpreter */
#define AT_FLAGS 8 /* Flags */
#define AT_ENTRY 9 /* Entry point of program */
#define AT_NOTELF 10 /* Program is not ELF */
#define AT_UID 11 /* Real uid */
#define AT_EUID 12 /* Effective uid */
#define AT_GID 13 /* Real gid */
#define AT_EGID 14 /* Effective gid */
#define AT_CLKTCK 17 /* Frequency of times() */
...
/* This entry gives some information about the FPU initialization
performed by the kernel. */
#define AT_FPUCW 18 /* Used FPU control word. */
...
/* A special ignored value for PPC, used by the kernel to control the
interpretation of the AUXV. Must be > 16. */
#define AT_IGNOREPPC 22 /* Entry should be ignored. */
#define AT_SECURE 23 /* Boolean, was exec setuid-like? */
#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/
#define AT_RANDOM 25 /* Address of 16 random bytes. */
#define AT_HWCAP2 26 /* More machine-dependent hints about
processor capabilities. */
#define AT_EXECFN 31 /* Filename of executable. */
...
我们关注的是这里面的AT_RANDOM,在libc中,它和生成canary有着紧密的关系。
static inline uintptr_t __attribute__ ((always_inline))
_dl_setup_stack_chk_guard (void *dl_random)
{
union
{
uintptr_t num;
unsigned char bytes[sizeof (uintptr_t)];
} ret = { 0 };
if (dl_random == NULL)
{
ret.bytes[sizeof (ret) - 1] = 255;
ret.bytes[sizeof (ret) - 2] = '\n';
}
else
{
memcpy (ret.bytes, dl_random, sizeof (ret));
#if BYTE_ORDER == LITTLE_ENDIAN
ret.num &= ~(uintptr_t) 0xff;
#elif BYTE_ORDER == BIG_ENDIAN
ret.num &= ~((uintptr_t) 0xff << (8 * (sizeof (ret) - 1)));
#else
#error "BYTE_ORDER unknown"
#endif
}
return ret.num;
}
也就是说,canary是通过dl_random上设置的。
通过测试和文章分析可知,AUXV结构体中AT_RANDOM的值对应了canary的值(The value is a pointer to sixteen random bytes provided by the kernel. The dynamic linker uses this to implement a stack canary)
上面的这篇文章也介绍了通过程序获取auxv的方法
#include <sys/auxv.h> unsigned long int getauxval(unsigned long int type);其中,type传入auxv的a_type即可。
注意:上传附件及图片大小不得大于30M。
⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。
