728x90
반응형
Full Code
#include <err.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stddef.h>
#define INSN_ENDBR64 (0xF30F1EFA) /* endbr64 */
#define CFI(f) \
({ \
if (__builtin_bswap32(*(uint32_t*)(f)) != INSN_ENDBR64) \
__builtin_trap(); \
(f); \
})
#define KEY_SIZE 0x20
typedef struct {
char key[KEY_SIZE];
char buf[KEY_SIZE];
const char *error;
int status;
void (*throw)(int, const char*, ...);
} ctx_t;
void read_member(ctx_t *ctx, off_t offset, size_t size) {
if (read(STDIN_FILENO, (void*)ctx + offset, size) <= 0) {
ctx->status = EXIT_FAILURE;
ctx->error = "I/O Error";
}
ctx->buf[strcspn(ctx->buf, "\n")] = '\0';
if (ctx->status != 0)
CFI(ctx->throw)(ctx->status, ctx->error);
}
void encrypt(ctx_t *ctx) {
for (size_t i = 0; i < KEY_SIZE; i++)
ctx->buf[i] ^= ctx->key[i];
}
int main() {
ctx_t ctx = { .error = NULL, .status = 0, .throw = err };
read_member(&ctx, offsetof(ctx_t, key), sizeof(ctx));
read_member(&ctx, offsetof(ctx_t, buf), sizeof(ctx));
encrypt(&ctx);
write(STDOUT_FILENO, ctx.buf, KEY_SIZE);
return 0;
}
code analysis
#define KEY_SIZE 0x20
typedef struct {
char key[KEY_SIZE];
char buf[KEY_SIZE];
const char *error;
int status;
void (*throw)(int, const char*, ...);
} ctx_t;
구조체 는 이렇다.
크기는 0x58 정도 이다.
int main() {
ctx_t ctx = { .error = NULL, .status = 0, .throw = err };
read_member(&ctx, offsetof(ctx_t, key), sizeof(ctx));
read_member(&ctx, offsetof(ctx_t, buf), sizeof(ctx));
encrypt(&ctx);
write(STDOUT_FILENO, ctx.buf, KEY_SIZE);
return 0;
}
ctx 는 error 에 NULL, status 에 0 throw 에 err 로 초기화 된다.
다음에 read_member 로 입력을 받는데
ctx_t 구조체의 key 위 offset 을 offset 인자로 들어간다.
size로 ctx 가 들어간다.
void read_member(ctx_t *ctx, off_t offset, size_t size) {
if (read(STDIN_FILENO, (void*)ctx + offset, size) <= 0) {
ctx->status = EXIT_FAILURE;
ctx->error = "I/O Error";
}
ctx->buf[strcspn(ctx->buf, "\n")] = '\0';
if (ctx->status != 0)
CFI(ctx->throw)(ctx->status, ctx->error);
}
read_member 함수에서는
ctx+offset 으로 입력을 받는다.
근데 여기서 size 가 ctx 구조체의 크기이기 때문에 bof가 발생할 수 있다.
#define INSN_ENDBR64 (0xF30F1EFA) /* endbr64 */
#define CFI(f) \
({ \
if (__builtin_bswap32(*(uint32_t*)(f)) != INSN_ENDBR64) \
__builtin_trap(); \
(f); \
})
CFI 매크로의 경우에는 throw 로 넘겨진 주소를 호출한다.
이떄 bswap 해서 endbr64 가 아니면 trap을 건다.
read_member(&ctx, offsetof(ctx_t, buf), sizeof(ctx));
ctx 구조체의 buf 멤버에 입력을 한다.
void encrypt(ctx_t *ctx) {
for (size_t i = 0; i < KEY_SIZE; i++)
ctx->buf[i] ^= ctx->key[i]; // buf key 인덱스 해서 xor 작업 해서 buf에 넣는다.
}
이후에 encrypt 함수로
buf와 key 를 xor 해서 buf 에 넣는다.
write(STDOUT_FILENO, ctx.buf, KEY_SIZE);
이후에 write 함수로 ctx의 buf 를 출력해준다.
solution
ctx 의 status 가 0이 아니라면 CFI 메크로가 실행 되기 때문에
status 에 0이 아닌 값을 준다.
CFI 메크로에서 bswap32 조건을 통과 해야 하는데 이는 endbr64 명령줄이 있는 함수로 throw 를 줘야 한다.
그래서 그냥 system 함수를 throw 로 주고 const char * 형인 error 에 “/bin/sh\\x00” 을 줬는데 endbr 을 통과 못한다.
bswap 을 하면 endbr64 인 0xf30f1efa 가 아닌 0xcc0f1efa 가 되는데 cc 가 어디서 나온 것 인지 모르겠다.
이 글은 옵시디언을 이용해서 작성되었습니다.
728x90
반응형
'TOOR' 카테고리의 다른 글
[TOOR] 11.2. One (Shot) gadget (0) | 2023.09.24 |
---|---|
[TOOR] 11.1. GOT Overwrite (0) | 2023.09.24 |
[TOOR] 9.2 ASLR_1 write up (0) | 2023.09.24 |
[TOOR] 9.1. ASLR (0) | 2023.09.20 |
[TOOR] 8,10 공유 라이브러리 & Lazy Binding & Now Binding (0) | 2023.09.20 |